<commit_before>import Ember from 'ember';
import layout from './template';

import loadAll from 'ember-osf/utils/load-relationship';
import outsideClick from 'ember-osf/utils/outside-click';
import Analytics from '../../mixins/analytics';
import pathJoin from 'ember-osf/utils/path-join';

/**
 * File browser widget
 *
 * Sample usage:
 * ```handlebars
 * {{file-browser}}
 * ```
 * @class file-browser
 */
export default Ember.Component.extend(Analytics, {
    // TODO: Improve documentation in the future
    layout,
    //Can be overwritten to have a trimmed down display, these are all the options available to be displayed
    display: Ember.A(['header', 'size-column', 'version-column', 'downloads-column', 'modified-column', 'delete-button', 'rename-button', 'download-button', 'view-button', 'info-button', 'upload-button', 'share-button']),
    store: Ember.inject.service(),
    toast: Ember.inject.service(),
    classNames: ['file-browser'],
    dropzoneOptions: {
        createImageThumbnails: false,
        method: 'PUT',
        withCredentials: true,
        preventMultipleFiles: true,
        acceptDirectories: false
    },
    multiple: true,
    unselect: true,
    openOnSelect: false,
    selectedFile: null,
    init() {
        this._super(...arguments);
        this.set('_items', Ember.A());
        outsideClick(function() {
            this.send('dismissPop');
        }.bind(this));
        Ember.$(window).resize(function() {
            this.send('dismissPop');
        }.bind(this));
    },
    currentUser: Ember.inject.service(),
    dropzone: Ember.computed.alias('edit'),
    edit: Ember.computed('user', function() {
        return this.get('user.id') === this.get('currentUser.currentUserId');
    }),
    _loadFiles(user) {
        //pagination? when?
        loadAll(user, 'quickfiles', this.get('_items')).then(() => {
            this.set('loaded', true);
            this.set('_items', this.get('_items').sortBy('itemName'));
            this.get('_items').forEach(item => {
                if (this.get('selectedFile.id') && this.get('selectedFile.id') === item.id) {
                    item.isSelected = true;
                }
            });
        });
    },
    _loadUser:  Ember.on('init', Ember.observer('user', function() {
        let user = this.get('user');
        if (!user || this.get('loaded')) {
            return;
        }
        //items need to be reloaded when attrs are received
        //TODO: think about replacing _items with user.items, provided it's loaded properly
        let _load = user_ => {
            Ember.run(() => {
                this.set('_items', Ember.A());
                Ember.run.next(() => {
                    this._loadFiles(user_);
                });
            });
        };
        if (user.then) {
            user.then(user_ => {
                _load(user_);
            })
        } else {
            _load(user);
        }
    })),
    uploadUrl: Ember.computed('user', function() {
        return this.get('user.links.relationships.quickfiles.links.upload.href');
    }),
    downloadUrl: Ember.computed('user', function() {
        return this.get('user.links.relationships.quickfiles.links.download.href');
    }),
    loaded: false,
    filtering: false,
    renaming: false,
    sortingBy: 'itemName',
    sortingOrder: 'asc',
    uploading: Ember.A(),
    filter: null,
    modalOpen: false,
    popupOpen: false,
    itemsLoaded: true,
    selectedItems: Ember.computed.filterBy('items', 'isSelected', true),
    loadedChanged: Ember.observer('itemsLoaded', function() {
        let containerWidth = this.$().width();
        this.set('itemWidth', containerWidth);
    }),
    link: Ember.computed('selectedItems.firstObject.guid', function() {
        let guid = this.get('selectedItems.firstObject.guid');
        return guid ? pathJoin(window.location.origin, guid) : undefined;
    }),
    flash(item, message, type, time) {
        item.set('flash', {
            message,
            type: type || 'success'
        });
        Ember.run.later(() => item.set('flash', null), time || 2000);
    },
    items: Ember.computed('_items', 'textValue', 'filtering', 'sortingBy', 'sortingOrder', function() {
        //look at ways to use the api to filter
        let items = this.get('textValue') && this.get('filtering') ? this.get('_items').filter(i => i.get('name').toLowerCase().indexOf(this.get('textValue').toLowerCase()) !== -1) : this.get('_items');
        let sorted = items.sortBy(this.get('sortingBy'));
        return this.get('sortingOrder') === 'des' ? sorted.reverse() : sorted;
    }),
    textFieldOpen: Ember.computed('filtering', 'renaming', function() {
        return this.get('filtering') ? 'filtering' : (this.get('renaming') ? 'renaming' : false);
    }),
    nameColumnWidth: Ember.computed('display', function() {
        let display = this.get('display');
        let width = 6 + !display.includes('share-link-column') + !display.includes('size-column') + !display.includes('version-column') + !display.includes('downloads-column') + 2 * !display.includes('modified-column');
        if (!display.includes('header')) { //Allows scrollable elements to use extra space occupied by header
            let height = Ember.$('.file-browser-list').height();
            Ember.$('.file-browser-list').height(height + 50);
        }
        return width;
    }),
    browserState: Ember.computed('loaded', '_items', function() {
        return this.get('loaded') ? (this.get('_items').length ? (this.get('items').length ? 'show' : 'filtered') : 'empty') : 'loading';
    }),
    clickable: Ember.computed('browserState', function() {
        let clickable = ['.dz-upload-button'];
        if (this.get('browserState') == 'empty') {
            clickable.push('.file-browser-list');
        }
        return clickable;
    }),
    actions: {
        //dropzone listeners
        addedFile(_, __, file) {
            this.get('uploading').pushObject(file);
        },
        uploadProgress(_, __, file, progress) {
            Ember.$('#uploading-' + file.size).css('width', progress + '%');
        },
        dragStart() {
            this.set('dropping', true);
        },
        dragEnd() {
            this.set('dropping', false);
        },
        error(_, __, file, response) {
            let message = response.message === undefined ? response : response.message;
            this.get('uploading').removeObject(file);
            this.get('toast').error(message);
        },
        success(_, __, file, response) {
            this.send('track', 'upload', 'track', 'Quick Files - Upload');
            this.get('uploading').removeObject(file);
            let data = response.data.attributes;
            //OPTIONS (some not researched)
            //Ember store method for passing updated attributes (either with a query for the object, or iterating to find matching)
            //Manually updating the object based on new attrs
            //Making an additional request anytime success is done, finding the file detail page based on path
            let path = data.path; //THERE SHOULD BE A BETTER WAY OF DOING THIS
            let conflictingItem = false;
            for (let file of this.get('_items')) {
                if (path === file.get('path')) {
                    conflictingItem = file;
                    break;
                }
            }
            if (conflictingItem) {
                conflictingItem.setProperties({
                    size: data.size,
                    currentVersion: data.extra.version,
                    dateModified: data.modified_utc
                });
                return;
            }
            response.data.type = 'file'; //
            response.data.attributes.currentVersion = '1';
            // Strip prefix.
            response.data.id = response.data.id.replace(/^[^\/]*\//, '');
            let item = this.get('store').push(response);
            item.set('links', response.data.links); //Push doesnt pass it links
            this.get('_items').unshiftObject(item);
            this.notifyPropertyChange('_items');
            Ember.run.next(() => {
                this.flash(item, 'This file has been added.');
                this.get('toast').success('A file has been added');
            });
        },
        buildUrl(files) {
            let name = files[0].name;
            let conflictingItem = false;
            for (let file of this.get('_items')) {
                if (name === file.get('itemName')) {
                    conflictingItem = file;
                    break;
                }
            }
            if (conflictingItem) {
                return conflictingItem.get('links.upload');
            }
            return this.get('uploadUrl') + '?' + Ember.$.param({
                name: files[0].name
            });
        },
        selectItem(item) {
            if (this.get('openOnSelect')) {
                this.openFile(item, 'view');
            }
            this.set('renaming', false);
            this.set('popupOpen', false);
            if (this.get('selectedItems.length') > 1) {
                for (var item_ of this.get('selectedItems')) {
                    item_.set('isSelected', item_ === item);
                }
            } else if (this.get('selectedItems.length') ===  1) {
                if (item.get('isSelected') && this.get('unselect')) {
                    item.set('isSelected', false);
                    return;
                }
                let otherItem = this.get('selectedItems.firstObject');
                otherItem.set('isSelected', false);
            }
            item.set('isSelected', true);
            this.set('shiftAnchor', item);
        },
        selectMultiple(item, toggle) {
            if (!this.get('multiple')) {
                return;
            }
            this.set('renaming', false);
            this.set('popupOpen', false);
            if (toggle) {
                item.toggleProperty('isSelected');
            } else {
                let items = this.get('items');
                let anchor = this.get('shiftAnchor');
                if (anchor) {
                    let max = Math.max(items.indexOf(anchor), items.indexOf(item));
                    let min = Math.min(items.indexOf(anchor), items.indexOf(item));
                    for (var item_ of this.get('items')) {
                        item_.set('isSelected', item_ === item || item_ === anchor || (items.indexOf(item_) > min && items.indexOf(item_) < max));
                    }
                }
                item.set('isSelected', true);
            }
            Ember.run.next(this, function(){
                if (this.get('selectedItems.length') === 1) {
                    this.set('shiftAnchor', item)
                }
            });
        },
        viewItem() {
            let item = this.get('selectedItems.firstObject');
            this.openFile(item, 'view');
        },
        openItem(item, qparams) {
            this.openFile(item, qparams);
        },
        downloadItem() {
            let downloadLink = this.get('selectedItems.firstObject.links.download');
            window.location = downloadLink;
        },
        _deleteItem(item) {
            item.destroyRecord().then(() => {
                this.flash(item, 'This file has been deleted.', 'danger');
                Ember.run.later(() => {
                    this.get('_items').removeObject(item);
                    this.notifyPropertyChange('_items');
                }, 1800);
            }).catch(() => this.flash(item, 'Delete failed.', 'danger'));
        },
        deleteItem(){
            this.send('_deleteItem', this.get('selectedItems.firstObject'));
            this.set('modalOpen', false);
        },
        deleteItems() {
            for (let item_ of this.get('selectedItems')) {
                this.send('_deleteItem', item_);
            }
            this.set('modalOpen', false);
        },
        _rename(conflict) {
            let item = this.get('selectedItems.firstObject');
            this.set('modalOpen', false);
            item.rename(this.get('textValue'), conflict).then(() => {
                this.flash(item, 'Successfully renamed');
                if (conflict === 'replace') {
                    const replacedItem  = this.get('_conflictingItem');
                    if (!replacedItem) {
                        return;
                    }
                    this.flash(replacedItem, 'This file has been replaced', 'danger');
                    setTimeout(() => {
                        this.get('_items').removeObject(replacedItem);
                        this.notifyPropertyChange('_items');
                    }, 1800);
                    // Later to avoid flash() trying to set on a destroyed item.
                    Ember.run.later(() => replacedItem.unloadRecord(), 2200);
                }
            }).catch(() => this.flash(item, 'Failed to rename item', 'danger'));
            this.set('textValue', null);
            this.toggleProperty('renaming');
        },
        rename() {
            let rename = this.get('textValue');
            let conflict = false;
            for (let item of this.get('_items')) {
                if (item.get('itemName') === rename) {
                    if (item === this.get('selectedItems.firstObject')) {
                        this.set('textValue', null);
                        this.toggleProperty('renaming');
                        return;
                    }
                    conflict = true;
                    this.set('_conflictingItem', item);
                    break;
                }
            }
            if (conflict) {
                this.set('modalOpen', 'renameConflict');
            } else {
                this.send('_rename');
            }
        },
        cancelRename() {
            this.set('textValue', null);
            this.toggleProperty('renaming')
            this.set('modalOpen', false);
        },
        sort(by, order) {
            this.$('.sorting').removeClass('active');
            this.$(`.${by}${order}`).addClass('active');
            this.setProperties({
                sortingBy: by,
                sortingOrder: order
            })
        },
        toggleText(which) {
            this.set('textValue', which === 'renaming' ? this.get('selectedItems.firstObject.itemName') : null);
            this.toggleProperty(which);
        },
        openModal(modalType) {
            this.set('modalOpen', modalType);
        },
        closeModal() {
            this.set('modalOpen', false);
        },
        textValueKeypress() {
            if (this.get('renaming')) {
                this.send('rename');
            }
        },
        copyLink() {
            this.set('popupOpen', true);
            if (this.get('link')) {
                return;
            }
            this.get('selectedItems.firstObject').getGuid();
        },
        dismissPop() {
            this.set('popupOpen', false);
        }
    }
});
<commit_msg>Get upload error message by coalescing all possible response formats<commit_after>import Ember from 'ember';
import layout from './template';

import loadAll from 'ember-osf/utils/load-relationship';
import outsideClick from 'ember-osf/utils/outside-click';
import Analytics from '../../mixins/analytics';
import pathJoin from 'ember-osf/utils/path-join';

/**
 * File browser widget
 *
 * Sample usage:
 * ```handlebars
 * {{file-browser}}
 * ```
 * @class file-browser
 */
export default Ember.Component.extend(Analytics, {
    // TODO: Improve documentation in the future
    layout,
    //Can be overwritten to have a trimmed down display, these are all the options available to be displayed
    display: Ember.A(['header', 'size-column', 'version-column', 'downloads-column', 'modified-column', 'delete-button', 'rename-button', 'download-button', 'view-button', 'info-button', 'upload-button', 'share-button']),
    store: Ember.inject.service(),
    toast: Ember.inject.service(),
    classNames: ['file-browser'],
    dropzoneOptions: {
        createImageThumbnails: false,
        method: 'PUT',
        withCredentials: true,
        preventMultipleFiles: true,
        acceptDirectories: false
    },
    multiple: true,
    unselect: true,
    openOnSelect: false,
    selectedFile: null,
    init() {
        this._super(...arguments);
        this.set('_items', Ember.A());
        outsideClick(function() {
            this.send('dismissPop');
        }.bind(this));
        Ember.$(window).resize(function() {
            this.send('dismissPop');
        }.bind(this));
    },
    currentUser: Ember.inject.service(),
    dropzone: Ember.computed.alias('edit'),
    edit: Ember.computed('user', function() {
        return this.get('user.id') === this.get('currentUser.currentUserId');
    }),
    _loadFiles(user) {
        //pagination? when?
        loadAll(user, 'quickfiles', this.get('_items')).then(() => {
            this.set('loaded', true);
            this.set('_items', this.get('_items').sortBy('itemName'));
            this.get('_items').forEach(item => {
                if (this.get('selectedFile.id') && this.get('selectedFile.id') === item.id) {
                    item.isSelected = true;
                }
            });
        });
    },
    _loadUser:  Ember.on('init', Ember.observer('user', function() {
        let user = this.get('user');
        if (!user || this.get('loaded')) {
            return;
        }
        //items need to be reloaded when attrs are received
        //TODO: think about replacing _items with user.items, provided it's loaded properly
        let _load = user_ => {
            Ember.run(() => {
                this.set('_items', Ember.A());
                Ember.run.next(() => {
                    this._loadFiles(user_);
                });
            });
        };
        if (user.then) {
            user.then(user_ => {
                _load(user_);
            })
        } else {
            _load(user);
        }
    })),
    uploadUrl: Ember.computed('user', function() {
        return this.get('user.links.relationships.quickfiles.links.upload.href');
    }),
    downloadUrl: Ember.computed('user', function() {
        return this.get('user.links.relationships.quickfiles.links.download.href');
    }),
    loaded: false,
    filtering: false,
    renaming: false,
    sortingBy: 'itemName',
    sortingOrder: 'asc',
    uploading: Ember.A(),
    filter: null,
    modalOpen: false,
    popupOpen: false,
    itemsLoaded: true,
    selectedItems: Ember.computed.filterBy('items', 'isSelected', true),
    loadedChanged: Ember.observer('itemsLoaded', function() {
        let containerWidth = this.$().width();
        this.set('itemWidth', containerWidth);
    }),
    link: Ember.computed('selectedItems.firstObject.guid', function() {
        let guid = this.get('selectedItems.firstObject.guid');
        return guid ? pathJoin(window.location.origin, guid) : undefined;
    }),
    flash(item, message, type, time) {
        item.set('flash', {
            message,
            type: type || 'success'
        });
        Ember.run.later(() => item.set('flash', null), time || 2000);
    },
    items: Ember.computed('_items', 'textValue', 'filtering', 'sortingBy', 'sortingOrder', function() {
        //look at ways to use the api to filter
        let items = this.get('textValue') && this.get('filtering') ? this.get('_items').filter(i => i.get('name').toLowerCase().indexOf(this.get('textValue').toLowerCase()) !== -1) : this.get('_items');
        let sorted = items.sortBy(this.get('sortingBy'));
        return this.get('sortingOrder') === 'des' ? sorted.reverse() : sorted;
    }),
    textFieldOpen: Ember.computed('filtering', 'renaming', function() {
        return this.get('filtering') ? 'filtering' : (this.get('renaming') ? 'renaming' : false);
    }),
    nameColumnWidth: Ember.computed('display', function() {
        let display = this.get('display');
        let width = 6 + !display.includes('share-link-column') + !display.includes('size-column') + !display.includes('version-column') + !display.includes('downloads-column') + 2 * !display.includes('modified-column');
        if (!display.includes('header')) { //Allows scrollable elements to use extra space occupied by header
            let height = Ember.$('.file-browser-list').height();
            Ember.$('.file-browser-list').height(height + 50);
        }
        return width;
    }),
    browserState: Ember.computed('loaded', '_items', function() {
        return this.get('loaded') ? (this.get('_items').length ? (this.get('items').length ? 'show' : 'filtered') : 'empty') : 'loading';
    }),
    clickable: Ember.computed('browserState', function() {
        let clickable = ['.dz-upload-button'];
        if (this.get('browserState') == 'empty') {
            clickable.push('.file-browser-list');
        }
        return clickable;
    }),
    actions: {
        //dropzone listeners
        addedFile(_, __, file) {
            this.get('uploading').pushObject(file);
        },
        uploadProgress(_, __, file, progress) {
            Ember.$('#uploading-' + file.size).css('width', progress + '%');
        },
        dragStart() {
            this.set('dropping', true);
        },
        dragEnd() {
            this.set('dropping', false);
        },
        error(_, __, file, response) {
            this.get('uploading').removeObject(file);
            this.get('toast').error(response.message_long || response.message || response);
        },
        success(_, __, file, response) {
            this.send('track', 'upload', 'track', 'Quick Files - Upload');
            this.get('uploading').removeObject(file);
            let data = response.data.attributes;
            //OPTIONS (some not researched)
            //Ember store method for passing updated attributes (either with a query for the object, or iterating to find matching)
            //Manually updating the object based on new attrs
            //Making an additional request anytime success is done, finding the file detail page based on path
            let path = data.path; //THERE SHOULD BE A BETTER WAY OF DOING THIS
            let conflictingItem = false;
            for (let file of this.get('_items')) {
                if (path === file.get('path')) {
                    conflictingItem = file;
                    break;
                }
            }
            if (conflictingItem) {
                conflictingItem.setProperties({
                    size: data.size,
                    currentVersion: data.extra.version,
                    dateModified: data.modified_utc
                });
                return;
            }
            response.data.type = 'file'; //
            response.data.attributes.currentVersion = '1';
            // Strip prefix.
            response.data.id = response.data.id.replace(/^[^\/]*\//, '');
            let item = this.get('store').push(response);
            item.set('links', response.data.links); //Push doesnt pass it links
            this.get('_items').unshiftObject(item);
            this.notifyPropertyChange('_items');
            Ember.run.next(() => {
                this.flash(item, 'This file has been added.');
                this.get('toast').success('A file has been added');
            });
        },
        buildUrl(files) {
            let name = files[0].name;
            let conflictingItem = false;
            for (let file of this.get('_items')) {
                if (name === file.get('itemName')) {
                    conflictingItem = file;
                    break;
                }
            }
            if (conflictingItem) {
                return conflictingItem.get('links.upload');
            }
            return this.get('uploadUrl') + '?' + Ember.$.param({
                name: files[0].name
            });
        },
        selectItem(item) {
            if (this.get('openOnSelect')) {
                this.openFile(item, 'view');
            }
            this.set('renaming', false);
            this.set('popupOpen', false);
            if (this.get('selectedItems.length') > 1) {
                for (var item_ of this.get('selectedItems')) {
                    item_.set('isSelected', item_ === item);
                }
            } else if (this.get('selectedItems.length') ===  1) {
                if (item.get('isSelected') && this.get('unselect')) {
                    item.set('isSelected', false);
                    return;
                }
                let otherItem = this.get('selectedItems.firstObject');
                otherItem.set('isSelected', false);
            }
            item.set('isSelected', true);
            this.set('shiftAnchor', item);
        },
        selectMultiple(item, toggle) {
            if (!this.get('multiple')) {
                return;
            }
            this.set('renaming', false);
            this.set('popupOpen', false);
            if (toggle) {
                item.toggleProperty('isSelected');
            } else {
                let items = this.get('items');
                let anchor = this.get('shiftAnchor');
                if (anchor) {
                    let max = Math.max(items.indexOf(anchor), items.indexOf(item));
                    let min = Math.min(items.indexOf(anchor), items.indexOf(item));
                    for (var item_ of this.get('items')) {
                        item_.set('isSelected', item_ === item || item_ === anchor || (items.indexOf(item_) > min && items.indexOf(item_) < max));
                    }
                }
                item.set('isSelected', true);
            }
            Ember.run.next(this, function(){
                if (this.get('selectedItems.length') === 1) {
                    this.set('shiftAnchor', item)
                }
            });
        },
        viewItem() {
            let item = this.get('selectedItems.firstObject');
            this.openFile(item, 'view');
        },
        openItem(item, qparams) {
            this.openFile(item, qparams);
        },
        downloadItem() {
            let downloadLink = this.get('selectedItems.firstObject.links.download');
            window.location = downloadLink;
        },
        _deleteItem(item) {
            item.destroyRecord().then(() => {
                this.flash(item, 'This file has been deleted.', 'danger');
                Ember.run.later(() => {
                    this.get('_items').removeObject(item);
                    this.notifyPropertyChange('_items');
                }, 1800);
            }).catch(() => this.flash(item, 'Delete failed.', 'danger'));
        },
        deleteItem(){
            this.send('_deleteItem', this.get('selectedItems.firstObject'));
            this.set('modalOpen', false);
        },
        deleteItems() {
            for (let item_ of this.get('selectedItems')) {
                this.send('_deleteItem', item_);
            }
            this.set('modalOpen', false);
        },
        _rename(conflict) {
            let item = this.get('selectedItems.firstObject');
            this.set('modalOpen', false);
            item.rename(this.get('textValue'), conflict).then(() => {
                this.flash(item, 'Successfully renamed');
                if (conflict === 'replace') {
                    const replacedItem  = this.get('_conflictingItem');
                    if (!replacedItem) {
                        return;
                    }
                    this.flash(replacedItem, 'This file has been replaced', 'danger');
                    setTimeout(() => {
                        this.get('_items').removeObject(replacedItem);
                        this.notifyPropertyChange('_items');
                    }, 1800);
                    // Later to avoid flash() trying to set on a destroyed item.
                    Ember.run.later(() => replacedItem.unloadRecord(), 2200);
                }
            }).catch(() => this.flash(item, 'Failed to rename item', 'danger'));
            this.set('textValue', null);
            this.toggleProperty('renaming');
        },
        rename() {
            let rename = this.get('textValue');
            let conflict = false;
            for (let item of this.get('_items')) {
                if (item.get('itemName') === rename) {
                    if (item === this.get('selectedItems.firstObject')) {
                        this.set('textValue', null);
                        this.toggleProperty('renaming');
                        return;
                    }
                    conflict = true;
                    this.set('_conflictingItem', item);
                    break;
                }
            }
            if (conflict) {
                this.set('modalOpen', 'renameConflict');
            } else {
                this.send('_rename');
            }
        },
        cancelRename() {
            this.set('textValue', null);
            this.toggleProperty('renaming')
            this.set('modalOpen', false);
        },
        sort(by, order) {
            this.$('.sorting').removeClass('active');
            this.$(`.${by}${order}`).addClass('active');
            this.setProperties({
                sortingBy: by,
                sortingOrder: order
            })
        },
        toggleText(which) {
            this.set('textValue', which === 'renaming' ? this.get('selectedItems.firstObject.itemName') : null);
            this.toggleProperty(which);
        },
        openModal(modalType) {
            this.set('modalOpen', modalType);
        },
        closeModal() {
            this.set('modalOpen', false);
        },
        textValueKeypress() {
            if (this.get('renaming')) {
                this.send('rename');
            }
        },
        copyLink() {
            this.set('popupOpen', true);
            if (this.get('link')) {
                return;
            }
            this.get('selectedItems.firstObject').getGuid();
        },
        dismissPop() {
            this.set('popupOpen', false);
        }
    }
});
<|endoftext|>


<commit_before>/**
 *
 *  CS3283/4 storage.js
 *  This class acts as a facade for other developers to access to the database.
 *  The implementation is a non-SQL local storage to support the WebApp.
 *
 */

import constants from 'src/database/constants';
import entitiesCreator from 'src/database/create/entitiesCreator';
import query from 'src/database/retrieve/query';
import dataModifier from 'src/database/data-modifier/dataModifier';
import exceptions from 'src/database/exceptions';

let databaseName = constants.INDEXEDDB_NAME;

function createUserTable(isUserTableFirstInitialize) {
  if (typeof Promise === 'function') {
    return new Promise((resolve, reject) => {
      if (!isUserTableFirstInitialize) {
        entitiesCreator.createUserTable()
        .then(success => resolve(true))
        .catch(sqlError => reject(sqlError));
      } else {
        resolve(false);
      }
    });
  } else {
    throw new exceptions.PromiseFunctionNotDefined();
  }
}

function createOrganizationTable(isOrganizationTableFirstInitialize) {
  if (typeof Promise === 'function') {
    return new Promise((resolve, reject) => {
      if (!isOrganizationTableFirstInitialize) {
        entitiesCreator.createOrganizationTable()
        .then(success => resolve(true))
        .catch(sqlError => reject(sqlError));
      } else {
        resolve('false');
      }
    });
  } else {
    throw new exceptions.PromiseFunctionNotDefined();
  }
}

function createFolderTable(isFolderTableFirstInitialize) {
  if (typeof Promise === 'function') {
    return new Promise((resolve, reject) => {
      if (!isFolderTableFirstInitialize) {
        entitiesCreator.createFolderTable()
        .then(success => resolve(true))
        .catch(sqlError => reject(sqlError));
      } else {
        resolve('false');
      }
    });
  } else {
    throw new exceptions.PromiseFunctionNotDefined();
  }
}

function createFileManagerTable(isFileTableFirstInitialize) {
  if (typeof Promise === 'function') {
    return new Promise((resolve, reject) => {
      if (!isFileTableFirstInitialize) {
        entitiesCreator.createFileManagerTable()
        .then(success => resolve(true))
        .catch(sqlError => reject(sqlError));
      } else {
        resolve('false');
      }
    });
  } else {
    throw new exceptions.PromiseFunctionNotDefined();
  }
}

function creationOfTables() {
  if (typeof Promise === 'function') {
    return new Promise((resolve, reject) => {
      query.isTableExistsInDatabase(constants.ENTITIES_USER)
      .then(isUserTableFirstInitialize =>
        createUserTable(isUserTableFirstInitialize)
        .then(isSuccess => isSuccess)
        .catch(sqlError => reject(sqlError))
      )
      .then(() =>
        query.isTableExistsInDatabase(constants.ENTITIES_ORGANIZATION)
        .then(isOrganizationTableFirstInitialize =>
          createOrganizationTable(isOrganizationTableFirstInitialize))
          .then(isSuccess => isSuccess)
          .catch(sqlError => reject(sqlError))
        .catch(sqlError => reject(sqlError))
      )
      .then(() =>
        query.isTableExistsInDatabase(constants.ENTITIES_FOLDER)
        .then(isFolderTableFirstInitialize =>
          createFolderTable(isFolderTableFirstInitialize))
          .then(isSuccess => isSuccess)
          .catch(sqlError => reject(sqlError))
        .catch(sqlError => reject(sqlError))
      )
      .then(() =>
        query.isTableExistsInDatabase(constants.ENTITIES_FILE_MANAGER)
        .then(isFileTableFirstInitialize =>
          createFileManagerTable(isFileTableFirstInitialize)
          .then(isSuccess => isSuccess)
          .catch(sqlError => reject(sqlError))
        )
        .catch(sqlError => reject(sqlError))
      )
      .then(isSuccess => resolve(isSuccess))
      .catch(sqlError => reject(sqlError));
    });
  } else {
    throw new exceptions.PromiseFunctionNotDefined();
  }
}

export default class storage {
  static constructor() {}

  static initializeDatabase(newDatabaseName) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) => {
        databaseName = newDatabaseName || databaseName;
        return entitiesCreator.initializeDatabase(databaseName)
        .then(step1 =>
          creationOfTables()
          .then(isFirstInstance => isFirstInstance)
          .catch(sqlErr => reject(sqlErr)))
        .then((isFirstInstance) => {
          if (typeof isFirstInstance === 'boolean') {
            return entitiesCreator.fillUpDefaultData()
            .then(isFilledUp => resolve(isFilledUp))
            .catch(sqlErr => reject(sqlErr));
          } else {
            return resolve(true);
          }
        })
        .catch(sqlError => reject(sqlError));
      });
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static loadAllFilesAndFolders() {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        query.getAllFilesAndFolders()
        .then(returnedArr => resolve(returnedArr))
        .catch(sqlError => reject(sqlError))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  // Searching the filename and foldername ONLY
  static partialSearch(searchString) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        query.searchString(searchString)
        .then(returnedArr => resolve(returnedArr))
        .catch(sqlError => reject(sqlError))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static getList(folderId) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        query.loadFolder(folderId)
        .then(returnedArr => resolve(returnedArr))
        .catch(sqlError => reject(sqlError))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static loadFile(fileId) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        query.loadFile(fileId)
        .then(returnedArr => resolve(returnedArr))
        .catch(sqlError => reject(sqlError))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static saveFile(fileId, fileString) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        dataModifier.saveFile(fileId, fileString)
        .then(() => resolve())
        .catch(sqlError => reject(sqlError))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static createFile(organizationId, filePath, folderId) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        dataModifier.createNewFile(organizationId, filePath, folderId)
        .then(fileObject => resolve(fileObject))
        .catch(sqlErr => reject(sqlErr))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static moveFile(fileId, newPath) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        dataModifier.moveFile(fileId, newPath)
        .then(() => resolve())
        .catch(sqlErr => reject(sqlErr))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static renameFileName(fileId, newFileName) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        dataModifier.changeFileName(fileId, newFileName)
        .then(() => resolve())
        .catch(err => reject(err))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static deleteFile(fileId) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        dataModifier.deleteFile(fileId)
          .then(() => resolve())
          .catch(sqlError => reject(sqlError))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static createFolder(organizationId, folderPath, currentFolderId) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        dataModifier.createNewFolder(organizationId, folderPath, currentFolderId)
        .then(data => resolve(data))
        .catch(err => reject(err))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  // only delete folder for now without cascade delete
  static deleteFolder(folderId) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        dataModifier.deleteFolder(folderId)
        .then(() => resolve())
        .catch(sqlError => reject(sqlError))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static deleteAll(newDatabaseName) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) => {
        databaseName = newDatabaseName || databaseName;
        return dataModifier.deleteAllEntities(databaseName)
        .then(() => resolve())
        .catch(sqlError => reject(sqlError));
      });
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }
}
<commit_msg>Add renameFolderName function into storage.js<commit_after>/**
 *
 *  CS3283/4 storage.js
 *  This class acts as a facade for other developers to access to the database.
 *  The implementation is a non-SQL local storage to support the WebApp.
 *
 */

import constants from 'src/database/constants';
import entitiesCreator from 'src/database/create/entitiesCreator';
import query from 'src/database/retrieve/query';
import dataModifier from 'src/database/data-modifier/dataModifier';
import exceptions from 'src/database/exceptions';

let databaseName = constants.INDEXEDDB_NAME;

function createUserTable(isUserTableFirstInitialize) {
  if (typeof Promise === 'function') {
    return new Promise((resolve, reject) => {
      if (!isUserTableFirstInitialize) {
        entitiesCreator.createUserTable()
        .then(success => resolve(true))
        .catch(sqlError => reject(sqlError));
      } else {
        resolve(false);
      }
    });
  } else {
    throw new exceptions.PromiseFunctionNotDefined();
  }
}

function createOrganizationTable(isOrganizationTableFirstInitialize) {
  if (typeof Promise === 'function') {
    return new Promise((resolve, reject) => {
      if (!isOrganizationTableFirstInitialize) {
        entitiesCreator.createOrganizationTable()
        .then(success => resolve(true))
        .catch(sqlError => reject(sqlError));
      } else {
        resolve('false');
      }
    });
  } else {
    throw new exceptions.PromiseFunctionNotDefined();
  }
}

function createFolderTable(isFolderTableFirstInitialize) {
  if (typeof Promise === 'function') {
    return new Promise((resolve, reject) => {
      if (!isFolderTableFirstInitialize) {
        entitiesCreator.createFolderTable()
        .then(success => resolve(true))
        .catch(sqlError => reject(sqlError));
      } else {
        resolve('false');
      }
    });
  } else {
    throw new exceptions.PromiseFunctionNotDefined();
  }
}

function createFileManagerTable(isFileTableFirstInitialize) {
  if (typeof Promise === 'function') {
    return new Promise((resolve, reject) => {
      if (!isFileTableFirstInitialize) {
        entitiesCreator.createFileManagerTable()
        .then(success => resolve(true))
        .catch(sqlError => reject(sqlError));
      } else {
        resolve('false');
      }
    });
  } else {
    throw new exceptions.PromiseFunctionNotDefined();
  }
}

function creationOfTables() {
  if (typeof Promise === 'function') {
    return new Promise((resolve, reject) => {
      query.isTableExistsInDatabase(constants.ENTITIES_USER)
      .then(isUserTableFirstInitialize =>
        createUserTable(isUserTableFirstInitialize)
        .then(isSuccess => isSuccess)
        .catch(sqlError => reject(sqlError))
      )
      .then(() =>
        query.isTableExistsInDatabase(constants.ENTITIES_ORGANIZATION)
        .then(isOrganizationTableFirstInitialize =>
          createOrganizationTable(isOrganizationTableFirstInitialize))
          .then(isSuccess => isSuccess)
          .catch(sqlError => reject(sqlError))
        .catch(sqlError => reject(sqlError))
      )
      .then(() =>
        query.isTableExistsInDatabase(constants.ENTITIES_FOLDER)
        .then(isFolderTableFirstInitialize =>
          createFolderTable(isFolderTableFirstInitialize))
          .then(isSuccess => isSuccess)
          .catch(sqlError => reject(sqlError))
        .catch(sqlError => reject(sqlError))
      )
      .then(() =>
        query.isTableExistsInDatabase(constants.ENTITIES_FILE_MANAGER)
        .then(isFileTableFirstInitialize =>
          createFileManagerTable(isFileTableFirstInitialize)
          .then(isSuccess => isSuccess)
          .catch(sqlError => reject(sqlError))
        )
        .catch(sqlError => reject(sqlError))
      )
      .then(isSuccess => resolve(isSuccess))
      .catch(sqlError => reject(sqlError));
    });
  } else {
    throw new exceptions.PromiseFunctionNotDefined();
  }
}

export default class storage {
  static constructor() {}

  static initializeDatabase(newDatabaseName) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) => {
        databaseName = newDatabaseName || databaseName;
        return entitiesCreator.initializeDatabase(databaseName)
        .then(step1 =>
          creationOfTables()
          .then(isFirstInstance => isFirstInstance)
          .catch(sqlErr => reject(sqlErr)))
        .then((isFirstInstance) => {
          if (typeof isFirstInstance === 'boolean') {
            return entitiesCreator.fillUpDefaultData()
            .then(isFilledUp => resolve(isFilledUp))
            .catch(sqlErr => reject(sqlErr));
          } else {
            return resolve(true);
          }
        })
        .catch(sqlError => reject(sqlError));
      });
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static loadAllFilesAndFolders() {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        query.getAllFilesAndFolders()
        .then(returnedArr => resolve(returnedArr))
        .catch(sqlError => reject(sqlError))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  // Searching the filename and foldername ONLY
  static partialSearch(searchString) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        query.searchString(searchString)
        .then(returnedArr => resolve(returnedArr))
        .catch(sqlError => reject(sqlError))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static getList(folderId) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        query.loadFolder(folderId)
        .then(returnedArr => resolve(returnedArr))
        .catch(sqlError => reject(sqlError))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static loadFile(fileId) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        query.loadFile(fileId)
        .then(returnedArr => resolve(returnedArr))
        .catch(sqlError => reject(sqlError))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static saveFile(fileId, fileString) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        dataModifier.saveFile(fileId, fileString)
        .then(() => resolve())
        .catch(sqlError => reject(sqlError))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static createFile(organizationId, filePath, folderId) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        dataModifier.createNewFile(organizationId, filePath, folderId)
        .then(fileObject => resolve(fileObject))
        .catch(sqlErr => reject(sqlErr))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static moveFile(fileId, newPath) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        dataModifier.moveFile(fileId, newPath)
        .then(() => resolve())
        .catch(sqlErr => reject(sqlErr))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static renameFileName(fileId, newFileName) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        dataModifier.changeFileName(fileId, newFileName)
        .then(() => resolve())
        .catch(err => reject(err))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static renameFolderName(folderId, newFolderName) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        dataModifier.renameFolderName(folderId, newFolderName)
        .then(() => resolve())
        .catch(sqlErr => reject(sqlErr))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static deleteFile(fileId) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        dataModifier.deleteFile(fileId)
          .then(() => resolve())
          .catch(sqlError => reject(sqlError))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static createFolder(organizationId, folderPath, currentFolderId) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        dataModifier.createNewFolder(organizationId, folderPath, currentFolderId)
        .then(data => resolve(data))
        .catch(err => reject(err))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  // only delete folder for now without cascade delete
  static deleteFolder(folderId) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) =>
        dataModifier.deleteFolder(folderId)
        .then(() => resolve())
        .catch(sqlError => reject(sqlError))
      );
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static deleteAll(newDatabaseName) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) => {
        databaseName = newDatabaseName || databaseName;
        return dataModifier.deleteAllEntities(databaseName)
        .then(() => resolve())
        .catch(sqlError => reject(sqlError));
      });
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }
}
<|endoftext|>


<commit_before>/**
 * Copyright (C) 2013 Maxime Hadjinlian <maxime.hadjinlian@gmail.com>
 * All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

(function() {
  // Retain the value of the original onAdd and onRemove functions
  let originalOnAdd = L.Marker.prototype.onAdd;
  let originalOnRemove = L.Marker.prototype.onRemove;

  // Add bounceonAdd options
  L.Marker.mergeOptions({
    bounceOnAdd: false,
    bounceOnAddOptions: {
      duration: 1000,
      height: -1,
    },
    bounceOnAddCallback: function() {},
  });

  L.Marker.include({

    _toPoint: function(latlng) {
      return this._map.latLngToContainerPoint(latlng);
    },

    _toLatLng: function(point) {
      return this._map.containerPointToLatLng(point);
    },

    _motionStep: function(timestamp, opts) {
      let self = this;
      let timePassed = new Date() - opts.start;
      let progress = timePassed / opts.duration;

      if (progress > 1) {
        progress = 1;
      }
      let delta = self._easeOutBounce(progress);
      opts.step(delta);

      if (progress === 1) {
        opts.end();
        return;
      }

      self._animationId = L.Util.requestAnimFrame(function(timestamp) {
        self._motionStep(timestamp, opts);
      });
    },

    _bounceMotion: function(duration, callback) {
      let original = L.latLng(this._origLatlng);
      let start_y = this._dropPoint.y;
      let start_x = this._dropPoint.x;
      let distance = this._point.y - start_y;
      let self = this;

      L.Util.requestAnimFrame(function(timestamp) {
        self._motionStep(timestamp, {
          delay: 10,
          duration: duration || 1000, // 1 sec by default
          start: new Date(),
          step: function(delta) {
            self._dropPoint.y =
              start_y
            + (distance * delta)
            - (self._map.project(self._map.getCenter()).y - self._origMapCenter.y);
            self._dropPoint.x =
              start_x
            - (self._map.project(self._map.getCenter()).x - self._origMapCenter.x);
            self.setLatLng(self._toLatLng(self._dropPoint));
          },
          end: function() {
            self.setLatLng(original);
            if (typeof callback === 'function') callback();
          },
        });
      });
    },

    // Many thanks to Robert Penner for this function
    _easeOutBounce: function(pos) {
      if ((pos) < (1 / 2.75)) {
        return (7.5625 * pos * pos);
      } else if (pos < (2 / 2.75)) {
        return (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75);
      } else if (pos < (2.5 / 2.75)) {
        return (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375);
      } else {
        return (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375);
      }
    },

    // Bounce : if options.height in pixels is not specified, drop from top.
    // If options.duration is not specified animation is 1s long.
    bounce: function(options, endCallback) {
      if (typeof options === 'function') {
        endCallback = options;
        options = null;
      }
      options = options || {duration: 1000, height: -1};

      // backward compatibility
      if (typeof options === 'number') {
        options.duration = arguments[0];
        options.height = arguments[1];
      }

      // Keep original map center
      this._origMapCenter = this._map.project(this._map.getCenter());
      this._dropPoint = this._getDropPoint(options.height);
      this._bounceMotion(options.duration, endCallback);
    },

    // This will get you a drop point given a height.
    // If no height is given, the top y will be used.
    _getDropPoint: function(height) {
      // Get current coordidates in pixel
      this._point = this._toPoint(this._origLatlng);
      let top_y;
      if (height === undefined || height < 0) {
        top_y = this._toPoint(this._map.getBounds()._northEast).y;
      } else {
        top_y = this._point.y - height;
      }
      return new L.Point(this._point.x, top_y);
    },

    onAdd: function(map) {
      this._map = map;

      // Call leaflet original method to add the Marker to the map.
      originalOnAdd.call(this, map);

      // Keep original latitude and longitude
      this._origLatlng = this.getLatLng();

      if (this.options.bounceOnAdd === true) {
        this.bounce(this.options.bounceOnAddOptions, this.options.bounceOnAddCallback);
      }
    },

    onRemove: function(map) {
      // We may have modified the marker; so we need to place it where it
      // belongs so next time its coordinates are not changed.
      this.setLatLng(this._origLatlng);
      cancelAnimationFrame(this._animationId);

      originalOnRemove.call(this, map);
    },
  });
})();
<commit_msg>bouncemarker.js: remove unused timestamp<commit_after>/**
 * Copyright (C) 2013 Maxime Hadjinlian <maxime.hadjinlian@gmail.com>
 * All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

(function() {
  // Retain the value of the original onAdd and onRemove functions
  let originalOnAdd = L.Marker.prototype.onAdd;
  let originalOnRemove = L.Marker.prototype.onRemove;

  // Add bounceonAdd options
  L.Marker.mergeOptions({
    bounceOnAdd: false,
    bounceOnAddOptions: {
      duration: 1000,
      height: -1,
    },
    bounceOnAddCallback: function() {},
  });

  L.Marker.include({

    _toPoint: function(latlng) {
      return this._map.latLngToContainerPoint(latlng);
    },

    _toLatLng: function(point) {
      return this._map.containerPointToLatLng(point);
    },

    _motionStep: function(opts) {
      let self = this;
      let timePassed = new Date() - opts.start;
      let progress = timePassed / opts.duration;

      if (progress > 1) {
        progress = 1;
      }
      let delta = self._easeOutBounce(progress);
      opts.step(delta);

      if (progress === 1) {
        opts.end();
        return;
      }

      self._animationId = L.Util.requestAnimFrame(function(timestamp) {
        self._motionStep(opts);
      });
    },

    _bounceMotion: function(duration, callback) {
      let original = L.latLng(this._origLatlng);
      let start_y = this._dropPoint.y;
      let start_x = this._dropPoint.x;
      let distance = this._point.y - start_y;
      let self = this;

      self._animationId = L.Util.requestAnimFrame(function() {
        self._motionStep({
          duration: duration || 1000, // 1 sec by default
          start: new Date(),
          step: function(delta) {
            self._dropPoint.y =
              start_y
            + (distance * delta)
            - (self._map.project(self._map.getCenter()).y - self._origMapCenter.y);
            self._dropPoint.x =
              start_x
            - (self._map.project(self._map.getCenter()).x - self._origMapCenter.x);
            self.setLatLng(self._toLatLng(self._dropPoint));
          },
          end: function() {
            self.setLatLng(original);
            if (typeof callback === 'function') callback();
          },
        });
      });
    },

    // Many thanks to Robert Penner for this function
    _easeOutBounce: function(pos) {
      if ((pos) < (1 / 2.75)) {
        return (7.5625 * pos * pos);
      } else if (pos < (2 / 2.75)) {
        return (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75);
      } else if (pos < (2.5 / 2.75)) {
        return (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375);
      } else {
        return (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375);
      }
    },

    // Bounce : if options.height in pixels is not specified, drop from top.
    // If options.duration is not specified animation is 1s long.
    bounce: function(options, endCallback) {
      if (typeof options === 'function') {
        endCallback = options;
        options = null;
      }
      options = options || {duration: 1000, height: -1};

      // backward compatibility
      if (typeof options === 'number') {
        options.duration = arguments[0];
        options.height = arguments[1];
      }

      // Keep original map center
      this._origMapCenter = this._map.project(this._map.getCenter());
      this._dropPoint = this._getDropPoint(options.height);
      this._bounceMotion(options.duration, endCallback);
    },

    // This will get you a drop point given a height.
    // If no height is given, the top y will be used.
    _getDropPoint: function(height) {
      // Get current coordidates in pixel
      this._point = this._toPoint(this._origLatlng);
      let top_y;
      if (height === undefined || height < 0) {
        top_y = this._toPoint(this._map.getBounds()._northEast).y;
      } else {
        top_y = this._point.y - height;
      }
      return new L.Point(this._point.x, top_y);
    },

    onAdd: function(map) {
      this._map = map;

      // Call leaflet original method to add the Marker to the map.
      originalOnAdd.call(this, map);

      // Keep original latitude and longitude
      this._origLatlng = this.getLatLng();

      if (this.options.bounceOnAdd === true) {
        this.bounce(this.options.bounceOnAddOptions, this.options.bounceOnAddCallback);
      }
    },

    onRemove: function(map) {
      // We may have modified the marker; so we need to place it where it
      // belongs so next time its coordinates are not changed.
      this.setLatLng(this._origLatlng);
      cancelAnimationFrame(this._animationId);

      originalOnRemove.call(this, map);
    },
  });
})();
<|endoftext|>


<commit_before>'use strict';

const os = require('os');
const path = require('path');

const test = require('tape');
const readjson = require('readjson');

const root = '../../';
const dir = root + 'server/';
const config = require(dir + 'config');

const pathHomeConfig = path.join(os.homedir(), '.cloudcmd.json');
const pathConfig = path.join(__dirname, '..', '..', 'json', 'config.json');

const clean = (name) => {
    delete require.cache[require.resolve(name)];
};

function readConfig() {
    return readjson.sync.try(pathHomeConfig) || require(pathConfig);
}

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

test('config: manage', (t) => {
    t.equal(undefined, config(), 'should return "undefined"');
    t.end();
});

test('config: manage: get', (t) => {
    const editor = 'deepword';

    before({config: {editor}}, (port, after) => {
        t.equal(config('editor'), editor, 'should get config');
        t.end();
        after();
    });
});

test('config: manage: get', (t) => {
    const editor = 'deepword';
    const conf = {
        editor
    };

    before({config: conf}, (port, after) => {
        config('editor', 'dword');
        t.equal('dword', config('editor'), 'should set config');
        t.end();
        after();
    });
});

test('config: manage: get: *', (t) => {
    clean(dir + 'config');

    const config = require(dir + 'config');
    const data = config('*');
    const expected = Object.assign({}, require(pathConfig), readConfig());

    t.deepEqual(data, expected, 'should return config data');
    t.end();
});

<commit_msg>test(config) listen<commit_after>'use strict';

const os = require('os');
const path = require('path');

const test = require('tape');
const readjson = require('readjson');

const root = '../../';
const dir = root + 'server/';
const config = require(dir + 'config');

const pathHomeConfig = path.join(os.homedir(), '.cloudcmd.json');
const pathConfig = path.join(__dirname, '..', '..', 'json', 'config.json');

const clean = (name) => {
    delete require.cache[require.resolve(name)];
};

function readConfig() {
    return readjson.sync.try(pathHomeConfig) || require(pathConfig);
}

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

test('config: manage', (t) => {
    t.equal(undefined, config(), 'should return "undefined"');
    t.end();
});

test('config: manage: get', (t) => {
    const editor = 'deepword';

    before({config: {editor}}, (port, after) => {
        t.equal(config('editor'), editor, 'should get config');
        t.end();
        after();
    });
});

test('config: manage: get', (t) => {
    const editor = 'deepword';
    const conf = {
        editor
    };

    before({config: conf}, (port, after) => {
        config('editor', 'dword');
        t.equal('dword', config('editor'), 'should set config');
        t.end();
        after();
    });
});

test('config: manage: get: *', (t) => {
    clean(dir + 'config');

    const config = require(dir + 'config');
    const data = config('*');
    const expected = Object.assign({}, require(pathConfig), readConfig());

    t.deepEqual(data, expected, 'should return config data');
    t.end();
});

test('config: listen: no socket', (t) => {
    t.throws(config.listen, 'should throw when no socket');
    t.end();
});

test('config: listen: authCheck: not function', (t) => {
    const socket = {};
    const fn = () => config.listen(socket, 'hello');

    t.throws(fn, 'should throw when authCheck not function');
    t.end();
});

<|endoftext|>


<commit_before>
/**
 * @fileoverview Base64 test suite.
 *
 * @see {@link http://google.github.io/styleguide/javascriptguide.xml}
 * @see {@link developers.google.com/closure/compiler/docs/js-for-compiler}
 * @see {@link https://code.google.com/p/js-test-driver/}
 */

util.Base64TestCase = TestCase('Base64TestCase');

util.Base64TestCase.prototype.testBase64 = function() {
  var input = 'test';
  var expected = 'dGVzdA==';
  var encoded = util.Base64.encode(input);
  var decoded = util.Base64.decode(encoded);
  assertNotNull(encoded);
  assertNotNull(decoded);
  assertEquals(expected, encoded);
  assertEquals(input, decoded);
};
<commit_msg>updated jstest<commit_after>
/**
 * @fileoverview Base64 test suite.
 *
 * @see {@link http://google.github.io/styleguide/javascriptguide.xml}
 * @see {@link developers.google.com/closure/compiler/docs/js-for-compiler}
 * @see {@link https://code.google.com/p/js-test-driver/}
 */

util.Base64TestCase = TestCase('Base64TestCase');

util.Base64TestCase.prototype.testBase64 = function() {
   // clear native 'btoa' and 'atob' methods.
  dom.context = {btoa: null, atob: null};

  var input = 'test';
  var expected = 'dGVzdA==';
  var encoded = util.Base64.encode(input);
  var decoded = util.Base64.decode(encoded);

  assertNotNull(encoded);
  assertNotNull(decoded);
  assertEquals(expected, encoded);
  assertEquals(input, decoded);
};
<|endoftext|>


<commit_before>const R = require('ramda');

const BrehonContract = artifacts.require('./BrehonContract.sol');
const defaults = require('../config/deployment_settings.js').defaults;

const contractHelpers = require('../lib/contractHelpers.js');

const startContract = contractHelpers.startContract;
const assertError = contractHelpers.assertError;
const StagesEnum = contractHelpers.StagesEnum;
const startContractAndRaiseDispute = contractHelpers.startContractAndRaiseDispute;
const verifyEvent = contractHelpers.verifyEvent;
const getMinimumContractAmt = contractHelpers.getMinimumContractAmt;
const getSplitForPrimaryBrehon = contractHelpers.getPercentageSplit(defaults, 0);

const ResolutionStruct = {
  proposerAddr: 0,
  awardPartyA: 1,
  awardPartyB: 2,
  partyAAccepted: 3,
  partyBAccepted: 4,
};

/**
 * Spec:
 * + Must use verifyEvent method
 * + Error verification should happen via assertError
 * - Must check for all stages
 **/

contract('BrehonContract acceptSettlement should only be allowed at one of the conflict stages', (accounts) => {
  const settlement = {
    'partyA': getSplitForPrimaryBrehon(50),
    'partyB': getSplitForPrimaryBrehon(50)
  };
  it('by preventing it from being called at Negotiation stage', () => {
    let brehonContract;
    return BrehonContract.deployed()
      .then(function captureReference(instance) {
        brehonContract = instance;
        return instance;
      })
      .then(function acceptSettlement() {
        return brehonContract.acceptSettlement(
          settlement.partyA,
          settlement.partyB,
          { from: defaults.partyA_addr }
        );
      })
      .catch(assertError('Exception was not thrown when acceptSettlement was triggered at Negotiation stage'));
  });

  it('by preventing it from being called at Execution stage', () => {
    var brehonContract;
    return BrehonContract.deployed()
      .then(function captureReference(instance) {
        brehonContract = instance;
        return instance;
      })
      .then(startContract(
        [{
          addr: defaults.partyA_addr,
          value: getMinimumContractAmt(defaults)
        }], defaults.partyA_addr))
      .then(function proposeSettlement() {
        return brehonContract.proposeSettlement(
          settlement.partyA,
          settlement.partyB,
          {from: defaults.partyA_addr}
        );
      })
      .catch(assertError('Exception was not thrown when acceptSettlement was triggered at Execution stage'));
  });

  //TODO Add test for Resolved state
  it('by preventing it from being called at Resolved stage', () => {
    var brehonContract;
    return BrehonContract.deployed()
      .then(function captureReference(instance) {
        brehonContract = instance;
        return instance;
      })
      .then(startContractAndRaiseDispute(
        [{
          addr: defaults.partyA_addr,
          value: getMinimumContractAmt(defaults)
        }], defaults.partyA_addr, defaults.partyA_addr))
      .then(function adjudicate() {
        return brehonContract.adjudicate(
            getSplitForPrimaryBrehon(100),
            getSplitForPrimaryBrehon(0),
            {from: defaults.primaryBrehon_addr}
        );
      })
      .then(function raise2ndAppeal() {
        return brehonContract.raiseAppeal(
            {from: defaults.partyA_addr}
        );
      })
      .catch(assertError('Exception was not thrown when raiseAppeal() was triggerred at the AppealPeriod stage'));
  });
});

contract('BrehonContract should allow partyA to accepted a proposed settlement', (accounts) => {
  const settlement = {
    'partyA': getSplitForPrimaryBrehon(60),
    'partyB': getSplitForPrimaryBrehon(40)
  };
  it('at Dispute stage', () => {
    var brehonContract;
    return BrehonContract.deployed()
      .then(function captureReference(instance) {
        brehonContract = instance;
        return instance;
      })
      .then(startContractAndRaiseDispute(
        [{
          addr: defaults.partyA_addr,
          value: getMinimumContractAmt(defaults)
        }], defaults.partyA_addr, defaults.partyA_addr))
      .then(function proposeSettlement() {
        return brehonContract.proposeSettlement(
          settlement.partyA,
          settlement.partyB,
          { from: defaults.partyB_addr }
        );
      })
      .then(function acceptSettlement() {
        return brehonContract.acceptSettlement(
          settlement.partyA,
          settlement.partyB,
          { from: defaults.partyA_addr }
        );
      })
      .then(verifyEvent('DisputeResolved', {
        '_awardPartyA': settlement.partyA,
        '_awardPartyB': settlement.partyB,
      }))
      .then(function verifyDisputeResolution() {
        return brehonContract.stage.call().then((stage) => {
          assert.equal(stage, StagesEnum.Completed, 'acceptSettlement not correctly changed the state');
        });
      })
      .catch(function handleException(err) {
        console.log(err);
        assert.isNull(err, 'Exception was thrown when partyA tried to accept settlement');
      });
  });
});

contract('BrehonContract should allow partyB to accepted a proposed settlement', (accounts) => {
  const settlement = {
    'partyA': getSplitForPrimaryBrehon(40),
    'partyB': getSplitForPrimaryBrehon(60)
  };
  it('at Dispute stage', () => {
    var brehonContract;
    return BrehonContract.deployed()
      .then(function captureReference(instance) {
        brehonContract = instance;
        return instance;
      })
      .then(startContractAndRaiseDispute(
        [{
          addr: defaults.partyA_addr,
          value: getMinimumContractAmt(defaults)
        }], defaults.partyA_addr, defaults.partyA_addr))
      .then(function proposeSettlement() {
        return brehonContract.proposeSettlement(
          settlement.partyA,
          settlement.partyB,
          { from: defaults.partyA_addr }
        );
      })
      .then(function acceptSettlement() {
        return brehonContract.acceptSettlement(
          settlement.partyA,
          settlement.partyB,
          { from: defaults.partyB_addr }
        );
      })
      .then(verifyEvent('DisputeResolved', {
        '_awardPartyA': settlement.partyA,
        '_awardPartyB': settlement.partyB,
      }))
      .then(function verifyDisputeResolution() {
        return brehonContract.stage.call().then((stage) => {
          assert.equal(stage, StagesEnum.Completed, 'acceptSettlement not correctly changed the state');
        });
      })
      .catch(function handleException(err) {
        console.log(err);
        assert.isNull(err, 'Exception was thrown when partyA tried to accept settlement');
      });
  });
});
<commit_msg>- Split the stage testing mechanism into separate contract blocks<commit_after>const R = require('ramda');

const BrehonContract = artifacts.require('./BrehonContract.sol');
const defaults = require('../config/deployment_settings.js').defaults;

const contractHelpers = require('../lib/contractHelpers.js');

const startContract = contractHelpers.startContract;
const assertError = contractHelpers.assertError;
const StagesEnum = contractHelpers.StagesEnum;
const startContractAndRaiseDispute = contractHelpers.startContractAndRaiseDispute;
const verifyEvent = contractHelpers.verifyEvent;
const getMinimumContractAmt = contractHelpers.getMinimumContractAmt;
const getSplitForPrimaryBrehon = contractHelpers.getPercentageSplit(defaults, 0);

const ResolutionStruct = {
  proposerAddr: 0,
  awardPartyA: 1,
  awardPartyB: 2,
  partyAAccepted: 3,
  partyBAccepted: 4,
};

/**
 * Spec:
 * + Must use verifyEvent method
 * + Error verification should happen via assertError
 * - Must check for all stages
 **/

contract('BrehonContract acceptSettlement should only be allowed at one of the conflict stages', (accounts) => {
  const settlement = {
    'partyA': getSplitForPrimaryBrehon(50),
    'partyB': getSplitForPrimaryBrehon(50)
  };
  it('by preventing it from being called at Negotiation stage', () => {
    let brehonContract;
    return BrehonContract.deployed()
      .then(function captureReference(instance) {
        brehonContract = instance;
        return instance;
      })
      .then(function acceptSettlement() {
        return brehonContract.acceptSettlement(
          settlement.partyA,
          settlement.partyB,
          { from: defaults.partyA_addr }
        );
      })
      .catch(assertError('Exception was not thrown when acceptSettlement was triggered at Negotiation stage'));
  });
});

contract('BrehonContract acceptSettlement should only be allowed at one of the conflict stages', (accounts) => {
  const settlement = {
    'partyA': getSplitForPrimaryBrehon(50),
    'partyB': getSplitForPrimaryBrehon(50)
  };
  it('by preventing it from being called at Execution stage', () => {
    var brehonContract;
    return BrehonContract.deployed()
      .then(function captureReference(instance) {
        brehonContract = instance;
        return instance;
      })
      .then(startContract(
        [{
          addr: defaults.partyA_addr,
          value: getMinimumContractAmt(defaults)
        }], defaults.partyA_addr))
      .then(function proposeSettlement() {
        return brehonContract.proposeSettlement(
          settlement.partyA,
          settlement.partyB,
          {from: defaults.partyA_addr}
        );
      })
      .catch(assertError('Exception was not thrown when acceptSettlement was triggered at Execution stage'));
  });
});

contract('BrehonContract acceptSettlement should only be allowed at one of the conflict stages', (accounts) => {
  const settlement = {
    'partyA': getSplitForPrimaryBrehon(50),
    'partyB': getSplitForPrimaryBrehon(50)
  };
  it('by preventing it from being called at Completed stage', () => {
    var brehonContract;
    return BrehonContract.deployed()
      .then(function captureReference(instance) {
        brehonContract = instance;
        return instance;
      })
      .then(startContractAndRaiseDispute(
        [{
          addr: defaults.partyA_addr,
          value: getMinimumContractAmt(defaults)
        }], defaults.partyA_addr, defaults.partyA_addr))
      .then(function proposeSettlement() {
        return brehonContract.proposeSettlement(
          settlement.partyA,
          settlement.partyB,
          { from: defaults.partyB_addr }
        );
      })
      .then(function acceptSettlement() {
        return brehonContract.acceptSettlement(
          settlement.partyA,
          settlement.partyB,
          { from: defaults.partyA_addr }
        );
      })
      .then(function acceptSettlement() {
        return brehonContract.acceptSettlement(
          settlement.partyA,
          settlement.partyB,
          { from: defaults.partyA_addr }
        );
      })
      .catch(assertError('Exception was not thrown when raiseAppeal() was triggerred at the AppealPeriod stage'));
  });
});

contract('BrehonContract should allow partyA to accepted a proposed settlement', (accounts) => {
  const settlement = {
    'partyA': getSplitForPrimaryBrehon(60),
    'partyB': getSplitForPrimaryBrehon(40)
  };
  it('at Dispute stage', () => {
    var brehonContract;
    return BrehonContract.deployed()
      .then(function captureReference(instance) {
        brehonContract = instance;
        return instance;
      })
      .then(startContractAndRaiseDispute(
        [{
          addr: defaults.partyA_addr,
          value: getMinimumContractAmt(defaults)
        }], defaults.partyA_addr, defaults.partyA_addr))
      .then(function proposeSettlement() {
        return brehonContract.proposeSettlement(
          settlement.partyA,
          settlement.partyB,
          { from: defaults.partyB_addr }
        );
      })
      .then(function acceptSettlement() {
        return brehonContract.acceptSettlement(
          settlement.partyA,
          settlement.partyB,
          { from: defaults.partyA_addr }
        );
      })
      .then(verifyEvent('DisputeResolved', {
        '_awardPartyA': settlement.partyA,
        '_awardPartyB': settlement.partyB,
      }))
      .then(function verifyDisputeResolution() {
        return brehonContract.stage.call().then((stage) => {
          assert.equal(stage, StagesEnum.Completed, 'acceptSettlement not correctly changed the state');
        });
      })
      .catch(function handleException(err) {
        console.log(err);
        assert.isNull(err, 'Exception was thrown when partyA tried to accept settlement');
      });
  });
});

contract('BrehonContract should allow partyB to accepted a proposed settlement', (accounts) => {
  const settlement = {
    'partyA': getSplitForPrimaryBrehon(40),
    'partyB': getSplitForPrimaryBrehon(60)
  };
  it('at Dispute stage', () => {
    var brehonContract;
    return BrehonContract.deployed()
      .then(function captureReference(instance) {
        brehonContract = instance;
        return instance;
      })
      .then(startContractAndRaiseDispute(
        [{
          addr: defaults.partyA_addr,
          value: getMinimumContractAmt(defaults)
        }], defaults.partyA_addr, defaults.partyA_addr))
      .then(function proposeSettlement() {
        return brehonContract.proposeSettlement(
          settlement.partyA,
          settlement.partyB,
          { from: defaults.partyA_addr }
        );
      })
      .then(function acceptSettlement() {
        return brehonContract.acceptSettlement(
          settlement.partyA,
          settlement.partyB,
          { from: defaults.partyB_addr }
        );
      })
      .then(verifyEvent('DisputeResolved', {
        '_awardPartyA': settlement.partyA,
        '_awardPartyB': settlement.partyB,
      }))
      .then(function verifyDisputeResolution() {
        return brehonContract.stage.call().then((stage) => {
          assert.equal(stage, StagesEnum.Completed, 'acceptSettlement not correctly changed the state');
        });
      })
      .catch(function handleException(err) {
        console.log(err);
        assert.isNull(err, 'Exception was thrown when partyA tried to accept settlement');
      });
  });
});
<|endoftext|>


<commit_before>//@program
/*
  Copyright 2011-2014 Marvell Semiconductor, Inc.

  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.
*/
// Helpful BLE docs
// https://www.bluetooth.org/en-us/specification/adopted-specifications

// ----------------------------------------------------------------------------------
// Themes/ Global Vars
// ----------------------------------------------------------------------------------
var Pins = require("pins");
var FTHEME = require("themes/flat/theme");
THEME = require("themes/sample/theme");
for (var i in FTHEME)
	THEME[i] = FTHEME[i];
var CREATIONS = require("creations/creations");

var CONTROL = require('mobile/control');
var MODEL = require('mobile/model');
var SCREEN = require('mobile/screen');
var SCROLLER = require('mobile/scroller');
var TOOL = require('mobile/tool');

var COLORIFIC_HANDLE = 40;

include("common");

// ----------------------------------------------------------------------------------
// Styles
// ----------------------------------------------------------------------------------
var applicationStyle = new Style({font: "16px Fira Sans"});
var statusStyle = new Style({font:"bold 22px", color:"white", horizontal:"center", vertical:"bottom", lines:"1"});

// ----------------------------------------------------------------------------------
// Skins
// ----------------------------------------------------------------------------------
var blackSkin = new Skin({fill: "black"});
var wheelSkin = new Skin({texture: new Texture('./assets/wheel.png', 2), width: 240, height: 240, variants: 240, aspect: "fit"});
var whiteSkin = new Skin({fill: "white"});

// ----------------------------------------------------------------------------------
// BLE Behavior Functions
// ----------------------------------------------------------------------------------
var BLERGBLightOneServiceBehavior = Behavior.template({
	connect(container, peripheral) {
		if ("discovery" != this.state) return;
		this.changeState(container, "connect");
		var params = {
			address: peripheral.address,
			addressType: peripheral.addressType,
			intervals: {min: 8, max: 12},			// Fast connection interval for higher responsiveness
		}
		Pins.invoke("/ble/gapConnect", params);
	},
	onCreate(container, data) {
		this.data = data;
		this.buffer = new ArrayBuffer(9);
		this.payload = new Uint8Array(this.buffer);
		this.initialize(container);
	},
	onLightChanged(container){
		if (null == this.connection) return;

		var rgb = hsvToColor(this.data.hue, this.data.saturation, 0.5 + this.data.brightness * 0.5);
		var rgbVals = rgb.slice(4,(rgb.length-1)).split(",");

		var payload = this.payload;
		payload[6] = rgbVals[0]; //R
		payload[7] = rgbVals[1]; //G
		payload[8] = rgbVals[2]; //B

		var params = {
			connection: this.connection,
			characteristic: COLORIFIC_HANDLE,
			value: this.buffer
		};
		Pins.invoke("/ble/gattWriteWithoutResponse", params);
	},
	onDisplaying(container) {
		this.startScanning(container);
	},
	onPeripheralConnected(container, peripheral) {
		if (null == this.connection) {
			this.connection = peripheral.connection;
			this.changeState(container, "");

			this.data.BUSY.visible = false;
			this.data.CONTROLS.visible = true;
			container.distribute("onLightChanged");
		}
	},
	onPeripheralDisconnected(container, peripheral) {
		this.initialize(container);
		application.distribute("onDisconnected");
		this.startScanning(container);
	},
	onPeripheralDiscovered(container, peripheral) {
		if ("discovery" != this.state) return;
		var scanResponseData = peripheral.data;
		for (var i = 0, c = scanResponseData.length; i < c; ++i) {
			var entry = scanResponseData[i];
			if (0x09 == entry.flag && 'RGBLightOne' == entry.data) {	// Complete local name
				this.connect(container, peripheral);
				return;
			}
		}
	},
	changeState(container, state) {
		this.state = state;
		application.distribute("onStateChange", state);
	},
	initialize(container) {
		var payload = this.payload;
		payload[0] = 0x58;
		payload[1] = 0x01;
		payload[2] = 0x03;
		payload[3] = 0x01;
		payload[4] = 0x10; // White Brightness
		payload[5] = 0x00; // ?
		this.connection = null;
		this.changeState(container, "idle");
	},
	startScanning(container) {
		Pins.invoke("/ble/gapStartScanning");
		this.changeState(container, "discovery");
	},
});

// ----------------------------------------------------------------------------------
// Main screen / UI elements
// ----------------------------------------------------------------------------------

// Main UI container
var MainScreen = Container.template($ => ({
	left: 0, right: 0, top: 0, bottom: 0, skin: blackSkin, active: true,
	behavior: Behavior({
		onBackButton(container) {
			application.invoke(new Message("xkpr://shell/close?id=" + application.id));
		},
		onCreate(container, data) {
			this.data = data;
		},
		onDisplaying(container) {
			this.data.BUSY.visible = true;
			this.data.CONTROLS.visible = false;
		}
	}),
	contents: [
		CREATIONS.DynamicHeader($),
		Container($, {
			left: 0, right: 0, top: 32, bottom: 0,
			behavior : BLERGBLightOneServiceBehavior({
				onDisconnected(container) {
					this.data.BUSY.visible = true;
					this.data.CONTROLS.visible = false;
					this.data.STATUS.string = "";
				},
				onStateChange(container, state) {
					this.data.STATUS.string = state;
				}
			}),
			contents: [
				CREATIONS.BusyPicture($, {anchor: "BUSY", url: "./assets/spinner.png", top: 40}),
				Container($, { anchor: "CONTROLS", left: 0, right: 0, top: 0, bottom: 0,
					contents: [
						HueSlider($, {}),
					]
				}),
				Label($, {anchor: "STATUS", left: 20, right: 20, bottom: 5, style: statusStyle}),
			],
		})
	]
}));

// ----------------------------------------------------------------------------------
// Application
// ----------------------------------------------------------------------------------

application.behavior = Behavior({
	onBLENotification(response) {
		var notification = response.notification;
		if ("gap/discover" == notification) {
			var peripheral = response;
			application.distribute("onPeripheralDiscovered", peripheral);
		}
		else if ("gap/connect" == notification) {
			var peripheral = response;
			application.distribute("onPeripheralConnected", peripheral);
		}
		else if ("gap/disconnect" == notification) {
			var connection = response.connection;
			application.distribute("onPeripheralDisconnected", connection);
		}
	},
	onLaunch(application) {
 		Pins.configure({
            ble: {
                require: "/lowpan/ble"
            }
		}, success => this.onPinsConfigured(application, success));
	},
	onPinsConfigured(application, success) {
		if (success) {
			this.data = {
	        	title: "RGBLightOne",
	        	titleStyle: CREATIONS.whiteDynamicHeaderTitleStyle,
	        	colorable: true,
	        	hue: 1,
	        	brightness: .1,
	        	saturation: 1
			};
			Pins.when("ble", "notification", this.onBLENotification);
			application.add(new MainScreen(this.data));
		}
		else {
			throw new Error("Unable to configure BLE");
		}
	},
});<commit_msg>Document separator byte<commit_after>//@program
/*
  Copyright 2011-2014 Marvell Semiconductor, Inc.

  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.
*/
// Helpful BLE docs
// https://www.bluetooth.org/en-us/specification/adopted-specifications

// ----------------------------------------------------------------------------------
// Themes/ Global Vars
// ----------------------------------------------------------------------------------
var Pins = require("pins");
var FTHEME = require("themes/flat/theme");
THEME = require("themes/sample/theme");
for (var i in FTHEME)
	THEME[i] = FTHEME[i];
var CREATIONS = require("creations/creations");

var CONTROL = require('mobile/control');
var MODEL = require('mobile/model');
var SCREEN = require('mobile/screen');
var SCROLLER = require('mobile/scroller');
var TOOL = require('mobile/tool');

var COLORIFIC_HANDLE = 40;

include("common");

// ----------------------------------------------------------------------------------
// Styles
// ----------------------------------------------------------------------------------
var applicationStyle = new Style({font: "16px Fira Sans"});
var statusStyle = new Style({font:"bold 22px", color:"white", horizontal:"center", vertical:"bottom", lines:"1"});

// ----------------------------------------------------------------------------------
// Skins
// ----------------------------------------------------------------------------------
var blackSkin = new Skin({fill: "black"});
var wheelSkin = new Skin({texture: new Texture('./assets/wheel.png', 2), width: 240, height: 240, variants: 240, aspect: "fit"});
var whiteSkin = new Skin({fill: "white"});

// ----------------------------------------------------------------------------------
// BLE Behavior Functions
// ----------------------------------------------------------------------------------
var BLERGBLightOneServiceBehavior = Behavior.template({
	connect(container, peripheral) {
		if ("discovery" != this.state) return;
		this.changeState(container, "connect");
		var params = {
			address: peripheral.address,
			addressType: peripheral.addressType,
			intervals: {min: 8, max: 12},			// Fast connection interval for higher responsiveness
		}
		Pins.invoke("/ble/gapConnect", params);
	},
	onCreate(container, data) {
		this.data = data;
		this.buffer = new ArrayBuffer(9);
		this.payload = new Uint8Array(this.buffer);
		this.initialize(container);
	},
	onLightChanged(container){
		if (null == this.connection) return;

		var rgb = hsvToColor(this.data.hue, this.data.saturation, 0.5 + this.data.brightness * 0.5);
		var rgbVals = rgb.slice(4,(rgb.length-1)).split(",");

		var payload = this.payload;
		payload[6] = rgbVals[0]; //R
		payload[7] = rgbVals[1]; //G
		payload[8] = rgbVals[2]; //B

		var params = {
			connection: this.connection,
			characteristic: COLORIFIC_HANDLE,
			value: this.buffer
		};
		Pins.invoke("/ble/gattWriteWithoutResponse", params);
	},
	onDisplaying(container) {
		this.startScanning(container);
	},
	onPeripheralConnected(container, peripheral) {
		if (null == this.connection) {
			this.connection = peripheral.connection;
			this.changeState(container, "");

			this.data.BUSY.visible = false;
			this.data.CONTROLS.visible = true;
			container.distribute("onLightChanged");
		}
	},
	onPeripheralDisconnected(container, peripheral) {
		this.initialize(container);
		application.distribute("onDisconnected");
		this.startScanning(container);
	},
	onPeripheralDiscovered(container, peripheral) {
		if ("discovery" != this.state) return;
		var scanResponseData = peripheral.data;
		for (var i = 0, c = scanResponseData.length; i < c; ++i) {
			var entry = scanResponseData[i];
			if (0x09 == entry.flag && 'RGBLightOne' == entry.data) {	// Complete local name
				this.connect(container, peripheral);
				return;
			}
		}
	},
	changeState(container, state) {
		this.state = state;
		application.distribute("onStateChange", state);
	},
	initialize(container) {
		var payload = this.payload;
		payload[0] = 0x58;
		payload[1] = 0x01;
		payload[2] = 0x03;
		payload[3] = 0x01;
		payload[4] = 0x10; // White brightness
		payload[5] = 0x00; // Separator byte
		this.connection = null;
		this.changeState(container, "idle");
	},
	startScanning(container) {
		Pins.invoke("/ble/gapStartScanning");
		this.changeState(container, "discovery");
	},
});

// ----------------------------------------------------------------------------------
// Main screen / UI elements
// ----------------------------------------------------------------------------------

// Main UI container
var MainScreen = Container.template($ => ({
	left: 0, right: 0, top: 0, bottom: 0, skin: blackSkin, active: true,
	behavior: Behavior({
		onBackButton(container) {
			application.invoke(new Message("xkpr://shell/close?id=" + application.id));
		},
		onCreate(container, data) {
			this.data = data;
		},
		onDisplaying(container) {
			this.data.BUSY.visible = true;
			this.data.CONTROLS.visible = false;
		}
	}),
	contents: [
		CREATIONS.DynamicHeader($),
		Container($, {
			left: 0, right: 0, top: 32, bottom: 0,
			behavior : BLERGBLightOneServiceBehavior({
				onDisconnected(container) {
					this.data.BUSY.visible = true;
					this.data.CONTROLS.visible = false;
					this.data.STATUS.string = "";
				},
				onStateChange(container, state) {
					this.data.STATUS.string = state;
				}
			}),
			contents: [
				CREATIONS.BusyPicture($, {anchor: "BUSY", url: "./assets/spinner.png", top: 40}),
				Container($, { anchor: "CONTROLS", left: 0, right: 0, top: 0, bottom: 0,
					contents: [
						HueSlider($, {}),
					]
				}),
				Label($, {anchor: "STATUS", left: 20, right: 20, bottom: 5, style: statusStyle}),
			],
		})
	]
}));

// ----------------------------------------------------------------------------------
// Application
// ----------------------------------------------------------------------------------

application.behavior = Behavior({
	onBLENotification(response) {
		var notification = response.notification;
		if ("gap/discover" == notification) {
			var peripheral = response;
			application.distribute("onPeripheralDiscovered", peripheral);
		}
		else if ("gap/connect" == notification) {
			var peripheral = response;
			application.distribute("onPeripheralConnected", peripheral);
		}
		else if ("gap/disconnect" == notification) {
			var connection = response.connection;
			application.distribute("onPeripheralDisconnected", connection);
		}
	},
	onLaunch(application) {
 		Pins.configure({
            ble: {
                require: "/lowpan/ble"
            }
		}, success => this.onPinsConfigured(application, success));
	},
	onPinsConfigured(application, success) {
		if (success) {
			this.data = {
	        	title: "RGBLightOne",
	        	titleStyle: CREATIONS.whiteDynamicHeaderTitleStyle,
	        	colorable: true,
	        	hue: 1,
	        	brightness: .1,
	        	saturation: 1
			};
			Pins.when("ble", "notification", this.onBLENotification);
			application.add(new MainScreen(this.data));
		}
		else {
			throw new Error("Unable to configure BLE");
		}
	},
});<|endoftext|>