<commit_before>(function() {
    'use strict';

    angular.module('chaise.record')

    .factory('constants', [function(){
        return {
            defaultPageSize: 25,
            MAX_CONCURENT_REQUEST: 6
        };
    }])

    .factory('recordAppUtils',
             ['constants', 'DataUtils', 'Errors', 'ErrorService', '$log', 'logActions', 'modalBox', '$q', 'recordTableUtils', '$rootScope',
             function (constants, DataUtils, Errors, ErrorService, $log, logActions, modalBox, $q, recordTableUtils, $rootScope) {

        /**
         * returns true if we have free slots for requests.
         * @private
         */
        function _haveFreeSlot() {
            var res = $rootScope.recordFlowControl.occupiedSlots < $rootScope.recordFlowControl.maxRequests;
            if (!res) {
                $log.debug("No free slot available.");
            }
            return res;
        }

        /**
         * @private
         * Flow-control logic for record app.
         * This will go through different sections of the page and will update them
         * if it's needed to.
         */
        function _processRequests(isUpdate) {
            if (!_haveFreeSlot() || $rootScope.pauseRequests) return;

            if ($rootScope.isMainDirty) {
                readMainEntity().then(function (tuple) {
                    $rootScope.isMainDirty = false;
                    _processRequests(isUpdate);
                }).catch(function (err) {
                    // show modal with different text if 400 Query Timeout Error
                    if (err instanceof ERMrest.QueryTimeoutError) {
                        err.subMessage = err.message;
                        err.message = "The main entity cannot be retrieved.";
                        ErrorService.handleException(err, true);
                    } else {
                        if (DataUtils.isObjectAndKeyDefined(err.errorData, 'redirectPath')) {
                            var redirectLink = UriUtils.createRedirectLinkFromPath(err.errorData.redirectPath);
                            err.errorData.redirectUrl = redirectLink.replace('record', 'recordset');
                        }
                        throw err;
                    }
                });
                return;
            }

            var i = 0, model, logObject;

            // inline entities
            for (i = 0; i < $rootScope.columnModels.length && $rootScope.hasInline; i++) {
                model = $rootScope.columnModels[i];
                if (!model.isInline || !model.tableModel.dirtyResult) continue;
                if (!_haveFreeSlot()) return;
                model.tableModel.logObject = _getTableModelLogObject(model.tableModel, isUpdate ? logActions.recordInlineUpdate : logActions.recordInlineRead);
                recordTableUtils.updateMainEntity(model.tableModel, _processRequests, !isUpdate, false);
            }

            // main aggregates
            if ($rootScope.hasAggregate) {
                readMainAggregates(isUpdate);
            }

            // related entites
            for (i = 0; i < $rootScope.relatedTableModels.length; i++) {
                model = $rootScope.relatedTableModels[i];
                if (!model.tableModel.dirtyResult) continue;
                if (!_haveFreeSlot()) return;
                model.tableModel.logObject = _getTableModelLogObject(model.tableModel, isUpdate ? logActions.recordRelatedUpdate : logActions.recordRelatedRead);
                recordTableUtils.updateMainEntity(model.tableModel, _processRequests, !isUpdate, false);
            }

            // aggregates in inline
            for (i = 0; i < $rootScope.columnModels.length && $rootScope.hasInline; i++) {
                model = $rootScope.columnModels[i];
                if (!model.isInline || model.tableModel.dirtyResult) continue;
                if (!_haveFreeSlot()) return;
                logObject = _getTableModelLogObject(model.tableModel, isUpdate ? logActions.recordInlineAggregateUpdate : logActions.recordInlineAggregate);
                recordTableUtils.updateColumnAggregates(model.tableModel, _processRequests, logObject, !isUpdate);
            }

            // aggregates in related
            for (i = 0; i < $rootScope.relatedTableModels.length; i++) {
                model = $rootScope.relatedTableModels[i];
                if (model.tableModel.dirtyResult) continue;
                if (!_haveFreeSlot()) return;
                logObject = _getTableModelLogObject(model.tableModel, isUpdate ? logActions.recordRelatedAggregateUpdate : logActions.recordRelatedAggregate);
                recordTableUtils.updateColumnAggregates(model.tableModel, _processRequests, logObject, !isUpdate);
            }
        }

        /**
         * Read data for the main entity
         * @returns {Promise} It will be resolved with Page object.
         */
        function readMainEntity() {
            var defer = $q.defer();

            $rootScope.reference.read(1, {action: logActions.recordRead}).then(function (page) {
                $log.info("Page: ", page);

                /*
                *  recordSetLink should be used to present user with  an option in case of no data found/more data found(>1)
                *  This could be link to RECORDSET or SEARCH.
                */
                var recordSetLink = page.reference.unfilteredReference.contextualize.compact.appLink;
                var tableDisplayName = page.reference.displayname.value;
                if (page.tuples.length < 1) {
                    throw new Errors.noRecordError({}, tableDisplayName, recordSetLink);
                }
                else if(page.hasNext || page.hasPrevious){
                    throw new Errors.multipleRecordError(tableDisplayName, recordSetLink);
                }

                $rootScope.page = page;
                var tuple = $rootScope.tuple = page.tuples[0];

                // Used directly in the record-display directive
                $rootScope.recordDisplayname = tuple.displayname;

                // Collate tuple.isHTML and tuple.values into an array of objects
                // i.e. {isHTML: false, value: 'sample'}
                $rootScope.recordValues = [];
                tuple.values.forEach(function(value, index) {
                    $rootScope.recordValues.push({
                        isHTML: tuple.isHTML[index],
                        value: value
                    });
                });

                $rootScope.displayReady = true;
                defer.resolve(page);
            }).catch(function (err) {
                defer.reject(err);
            });

            return defer.promise;
        }

        /**
         * @private
         * creates the read request for aggregate columns of the main entity
         */
        function readMainAggregates(isUpdate) {
            $rootScope.columnModels.forEach(function (model, index) {
                if (!model.isAggregate || !_haveFreeSlot() || !model.dirtyResult) return;
                $rootScope.recordFlowControl.occupiedSlots++;
                model.dirtyResult = false;
                model.isLoading = true;
                _readMainColumnAggregate(model.column, index, $rootScope.recordFlowControl.counter).then(function (res) {
                    $rootScope.recordFlowControl.occupiedSlots--;
                    model.dirtyResult = !res;
                    _processRequests(isUpdate);
                }).catch(function (err) {
                    model.isLoading = false;
                    if (err instanceof ERMrest.QueryTimeoutError) {
                        // TODO: how to show column aggregates error
                        // is this columns in a related table? should it be `!` on the column header?
                        model.hasError = true;
                    } else {
                        if (DataUtils.isObjectAndKeyDefined(err.errorData, 'redirectPath')) {
                            var redirectLink = UriUtils.createRedirectLinkFromPath(err.errorData.redirectPath);
                            err.errorData.redirectUrl = redirectLink.replace('record', 'recordset');
                        }
                        throw err;
                    }
                });
            });
        }

        /**
         * @private
         * Generate request for each individual aggregate columns.
         * Returns a promise. The resolved value denotes the success or failure.
         */
        function _readMainColumnAggregate(column, index, current) {
            var defer = $q.defer();
            var logObject = column.reference.defaultLogInfo;
            logObject.action = logActions.recordAggregate;
            logObject.referrer = $rootScope.reference.defaultLogInfo;

            column.getAggregatedValue($rootScope.page, logObject).then(function (values) {
                $rootScope.columnModels[index].isLoading = false;
                if ($rootScope.recordFlowControl.counter !== current) {
                    return defer.resolve(false);
                }
                $rootScope.recordValues[index] = values[0];
                return defer.resolve(true);
            }).catch(function (err) {
                if ($rootScope.recordFlowControl.counter !== current) {
                    return defer.resolve(false);
                }
                return defer.reject(err);
            });
            return defer.promise;
        }

        /**
         * sets the flag and calls the flow-control function to update the record page.
         * @param  {Boolean} isUpdate indicates that the function has been triggered for update and not load.
         */
        function updateRecordPage(isUpdate) {

            if (!isUpdate) {
                $rootScope.recordFlowControl.occupiedSlots = 0;
                $rootScope.recordFlowControl.counter = 0;
            } else {
                // we want to update the main entity on update
                $rootScope.isMainDirty = true;
            }
            $rootScope.recordFlowControl.counter++;

            $rootScope.columnModels.forEach(function (m) {
                if (m.isAggregate) {
                    m.dirtyResult = true;
                } else if (m.isInline) {
                    m.tableModel.dirtyResult = true;
                }
            });
            $rootScope.relatedTableModels.forEach(function (m) {
                m.tableModel.dirtyResult = true;
            });

            $rootScope.pauseRequests = false;
            _processRequests(isUpdate);
        }

        /**
         * will pause the requests that are pending for updating the page.
         * Currently it's only setting a variable, but we might want to add
         * more logic later.
         */
        function pauseUpdateRecordPage() {
            $rootScope.pauseRequests = true;
        }

        /**
         * Resume the requests after pausing
         */
        function resumeUpdateRecordPage() {
            if (!$rootScope.pauseRequests) return;
            $rootScope.pauseRequests = false;
            _processRequests(true);
        }

        /**
         * The genetic error catch for record app.
         * @param  {object} exception error object
         */
        function genericErrorCatch(exception) {
            if (DataUtils.isObjectAndKeyDefined(exception.errorData, 'redirectPath')) {
                var redirectLink = UriUtils.createRedirectLinkFromPath(exception.errorData.redirectPath);
                exception.errorData.redirectUrl = redirectLink.replace('record', 'recordset');
            }
            throw exception;
        }

        /**
         * Given reference of related or inline, will create appropriate table model.
         * @param  {ERMrest.Reference} reference Reference object.
         * @param  {string} context   the context string
         * @param  {ERMrest.tuple} fromTuple the main tuple
         */
        function getTableModel (reference, context, fromTuple) {
            return {
                reference: reference,
                pageLimit: getPageSize(reference),
                displayType: reference.display.type,
                fromTuple: fromTuple,
                context: context,
                enableSort: true,
                rowValues: [],
                selectedRows: [],//TODO migth not be needed
                dirtyResult: true,
                isLoading: true,
                tableError: false,
                config: {
                    viewable: true,
                    editable: $rootScope.modifyRecord,
                    deletable: $rootScope.modifyRecord && $rootScope.showDeleteButton,
                    selectMode: modalBox.noSelect
                },
                flowControlObject: $rootScope.recordFlowControl
            };
        }

        /**
         * @private
         * Returns appropriate log object, for the related or inline table model.
         */
        function _getTableModelLogObject(tableModel, action) {
            var logObject = tableModel.reference.defaultLogInfo;
            logObject.referrer = $rootScope.reference.defaultLogInfo;
            logObject.action = action;
            return logObject;
        }

        /**
         * @private
         * returns page size of the display attribute in the object.
         * @param {ERMrest.Reference} reference Object reference that has the display attribute
         */
        function getPageSize(reference){
            return ((!angular.isUndefined(reference) && reference.display.defaultPageSize) ? reference.display.defaultPageSize:constants.defaultPageSize);
        }

        function FlowControlObject (maxRequests) {
            maxRequests = maxRequests || constants.MAX_CONCURENT_REQUEST;
            recordTableUtils.FlowControlObject.call(this, maxRequests);
        }

        return {
            updateRecordPage: updateRecordPage,
            genericErrorCatch: genericErrorCatch,
            readMainEntity: readMainEntity,
            getTableModel: getTableModel,
            FlowControlObject: FlowControlObject,
            pauseUpdateRecordPage: pauseUpdateRecordPage,
            resumeUpdateRecordPage: resumeUpdateRecordPage
        };
    }]);

})();
<commit_msg>remove comment<commit_after>(function() {
    'use strict';

    angular.module('chaise.record')

    .factory('constants', [function(){
        return {
            defaultPageSize: 25,
            MAX_CONCURENT_REQUEST: 6
        };
    }])

    .factory('recordAppUtils',
             ['constants', 'DataUtils', 'Errors', 'ErrorService', '$log', 'logActions', 'modalBox', '$q', 'recordTableUtils', '$rootScope',
             function (constants, DataUtils, Errors, ErrorService, $log, logActions, modalBox, $q, recordTableUtils, $rootScope) {

        /**
         * returns true if we have free slots for requests.
         * @private
         */
        function _haveFreeSlot() {
            var res = $rootScope.recordFlowControl.occupiedSlots < $rootScope.recordFlowControl.maxRequests;
            if (!res) {
                $log.debug("No free slot available.");
            }
            return res;
        }

        /**
         * @private
         * Flow-control logic for record app.
         * This will go through different sections of the page and will update them
         * if it's needed to.
         */
        function _processRequests(isUpdate) {
            if (!_haveFreeSlot() || $rootScope.pauseRequests) return;

            if ($rootScope.isMainDirty) {
                readMainEntity().then(function (tuple) {
                    $rootScope.isMainDirty = false;
                    _processRequests(isUpdate);
                }).catch(function (err) {
                    // show modal with different text if 400 Query Timeout Error
                    if (err instanceof ERMrest.QueryTimeoutError) {
                        err.subMessage = err.message;
                        err.message = "The main entity cannot be retrieved.";
                        ErrorService.handleException(err, true);
                    } else {
                        if (DataUtils.isObjectAndKeyDefined(err.errorData, 'redirectPath')) {
                            var redirectLink = UriUtils.createRedirectLinkFromPath(err.errorData.redirectPath);
                            err.errorData.redirectUrl = redirectLink.replace('record', 'recordset');
                        }
                        throw err;
                    }
                });
                return;
            }

            var i = 0, model, logObject;

            // inline entities
            for (i = 0; i < $rootScope.columnModels.length && $rootScope.hasInline; i++) {
                model = $rootScope.columnModels[i];
                if (!model.isInline || !model.tableModel.dirtyResult) continue;
                if (!_haveFreeSlot()) return;
                model.tableModel.logObject = _getTableModelLogObject(model.tableModel, isUpdate ? logActions.recordInlineUpdate : logActions.recordInlineRead);
                recordTableUtils.updateMainEntity(model.tableModel, _processRequests, !isUpdate, false);
            }

            // main aggregates
            if ($rootScope.hasAggregate) {
                readMainAggregates(isUpdate);
            }

            // related entites
            for (i = 0; i < $rootScope.relatedTableModels.length; i++) {
                model = $rootScope.relatedTableModels[i];
                if (!model.tableModel.dirtyResult) continue;
                if (!_haveFreeSlot()) return;
                model.tableModel.logObject = _getTableModelLogObject(model.tableModel, isUpdate ? logActions.recordRelatedUpdate : logActions.recordRelatedRead);
                recordTableUtils.updateMainEntity(model.tableModel, _processRequests, !isUpdate, false);
            }

            // aggregates in inline
            for (i = 0; i < $rootScope.columnModels.length && $rootScope.hasInline; i++) {
                model = $rootScope.columnModels[i];
                if (!model.isInline || model.tableModel.dirtyResult) continue;
                if (!_haveFreeSlot()) return;
                logObject = _getTableModelLogObject(model.tableModel, isUpdate ? logActions.recordInlineAggregateUpdate : logActions.recordInlineAggregate);
                recordTableUtils.updateColumnAggregates(model.tableModel, _processRequests, logObject, !isUpdate);
            }

            // aggregates in related
            for (i = 0; i < $rootScope.relatedTableModels.length; i++) {
                model = $rootScope.relatedTableModels[i];
                if (model.tableModel.dirtyResult) continue;
                if (!_haveFreeSlot()) return;
                logObject = _getTableModelLogObject(model.tableModel, isUpdate ? logActions.recordRelatedAggregateUpdate : logActions.recordRelatedAggregate);
                recordTableUtils.updateColumnAggregates(model.tableModel, _processRequests, logObject, !isUpdate);
            }
        }

        /**
         * Read data for the main entity
         * @returns {Promise} It will be resolved with Page object.
         */
        function readMainEntity() {
            var defer = $q.defer();

            $rootScope.reference.read(1, {action: logActions.recordRead}).then(function (page) {
                $log.info("Page: ", page);

                /*
                *  recordSetLink should be used to present user with  an option in case of no data found/more data found(>1)
                *  This could be link to RECORDSET or SEARCH.
                */
                var recordSetLink = page.reference.unfilteredReference.contextualize.compact.appLink;
                var tableDisplayName = page.reference.displayname.value;
                if (page.tuples.length < 1) {
                    throw new Errors.noRecordError({}, tableDisplayName, recordSetLink);
                }
                else if(page.hasNext || page.hasPrevious){
                    throw new Errors.multipleRecordError(tableDisplayName, recordSetLink);
                }

                $rootScope.page = page;
                var tuple = $rootScope.tuple = page.tuples[0];

                // Used directly in the record-display directive
                $rootScope.recordDisplayname = tuple.displayname;

                // Collate tuple.isHTML and tuple.values into an array of objects
                // i.e. {isHTML: false, value: 'sample'}
                $rootScope.recordValues = [];
                tuple.values.forEach(function(value, index) {
                    $rootScope.recordValues.push({
                        isHTML: tuple.isHTML[index],
                        value: value
                    });
                });

                $rootScope.displayReady = true;
                defer.resolve(page);
            }).catch(function (err) {
                defer.reject(err);
            });

            return defer.promise;
        }

        /**
         * @private
         * creates the read request for aggregate columns of the main entity
         */
        function readMainAggregates(isUpdate) {
            $rootScope.columnModels.forEach(function (model, index) {
                if (!model.isAggregate || !_haveFreeSlot() || !model.dirtyResult) return;
                $rootScope.recordFlowControl.occupiedSlots++;
                model.dirtyResult = false;
                model.isLoading = true;
                _readMainColumnAggregate(model.column, index, $rootScope.recordFlowControl.counter).then(function (res) {
                    $rootScope.recordFlowControl.occupiedSlots--;
                    model.dirtyResult = !res;
                    _processRequests(isUpdate);
                }).catch(function (err) {
                    model.isLoading = false;
                    if (err instanceof ERMrest.QueryTimeoutError) {
                        model.hasError = true;
                    } else {
                        if (DataUtils.isObjectAndKeyDefined(err.errorData, 'redirectPath')) {
                            var redirectLink = UriUtils.createRedirectLinkFromPath(err.errorData.redirectPath);
                            err.errorData.redirectUrl = redirectLink.replace('record', 'recordset');
                        }
                        throw err;
                    }
                });
            });
        }

        /**
         * @private
         * Generate request for each individual aggregate columns.
         * Returns a promise. The resolved value denotes the success or failure.
         */
        function _readMainColumnAggregate(column, index, current) {
            var defer = $q.defer();
            var logObject = column.reference.defaultLogInfo;
            logObject.action = logActions.recordAggregate;
            logObject.referrer = $rootScope.reference.defaultLogInfo;

            column.getAggregatedValue($rootScope.page, logObject).then(function (values) {
                $rootScope.columnModels[index].isLoading = false;
                if ($rootScope.recordFlowControl.counter !== current) {
                    return defer.resolve(false);
                }
                $rootScope.recordValues[index] = values[0];
                return defer.resolve(true);
            }).catch(function (err) {
                if ($rootScope.recordFlowControl.counter !== current) {
                    return defer.resolve(false);
                }
                return defer.reject(err);
            });
            return defer.promise;
        }

        /**
         * sets the flag and calls the flow-control function to update the record page.
         * @param  {Boolean} isUpdate indicates that the function has been triggered for update and not load.
         */
        function updateRecordPage(isUpdate) {

            if (!isUpdate) {
                $rootScope.recordFlowControl.occupiedSlots = 0;
                $rootScope.recordFlowControl.counter = 0;
            } else {
                // we want to update the main entity on update
                $rootScope.isMainDirty = true;
            }
            $rootScope.recordFlowControl.counter++;

            $rootScope.columnModels.forEach(function (m) {
                if (m.isAggregate) {
                    m.dirtyResult = true;
                } else if (m.isInline) {
                    m.tableModel.dirtyResult = true;
                }
            });
            $rootScope.relatedTableModels.forEach(function (m) {
                m.tableModel.dirtyResult = true;
            });

            $rootScope.pauseRequests = false;
            _processRequests(isUpdate);
        }

        /**
         * will pause the requests that are pending for updating the page.
         * Currently it's only setting a variable, but we might want to add
         * more logic later.
         */
        function pauseUpdateRecordPage() {
            $rootScope.pauseRequests = true;
        }

        /**
         * Resume the requests after pausing
         */
        function resumeUpdateRecordPage() {
            if (!$rootScope.pauseRequests) return;
            $rootScope.pauseRequests = false;
            _processRequests(true);
        }

        /**
         * The genetic error catch for record app.
         * @param  {object} exception error object
         */
        function genericErrorCatch(exception) {
            if (DataUtils.isObjectAndKeyDefined(exception.errorData, 'redirectPath')) {
                var redirectLink = UriUtils.createRedirectLinkFromPath(exception.errorData.redirectPath);
                exception.errorData.redirectUrl = redirectLink.replace('record', 'recordset');
            }
            throw exception;
        }

        /**
         * Given reference of related or inline, will create appropriate table model.
         * @param  {ERMrest.Reference} reference Reference object.
         * @param  {string} context   the context string
         * @param  {ERMrest.tuple} fromTuple the main tuple
         */
        function getTableModel (reference, context, fromTuple) {
            return {
                reference: reference,
                pageLimit: getPageSize(reference),
                displayType: reference.display.type,
                fromTuple: fromTuple,
                context: context,
                enableSort: true,
                rowValues: [],
                selectedRows: [],//TODO migth not be needed
                dirtyResult: true,
                isLoading: true,
                tableError: false,
                config: {
                    viewable: true,
                    editable: $rootScope.modifyRecord,
                    deletable: $rootScope.modifyRecord && $rootScope.showDeleteButton,
                    selectMode: modalBox.noSelect
                },
                flowControlObject: $rootScope.recordFlowControl
            };
        }

        /**
         * @private
         * Returns appropriate log object, for the related or inline table model.
         */
        function _getTableModelLogObject(tableModel, action) {
            var logObject = tableModel.reference.defaultLogInfo;
            logObject.referrer = $rootScope.reference.defaultLogInfo;
            logObject.action = action;
            return logObject;
        }

        /**
         * @private
         * returns page size of the display attribute in the object.
         * @param {ERMrest.Reference} reference Object reference that has the display attribute
         */
        function getPageSize(reference){
            return ((!angular.isUndefined(reference) && reference.display.defaultPageSize) ? reference.display.defaultPageSize:constants.defaultPageSize);
        }

        function FlowControlObject (maxRequests) {
            maxRequests = maxRequests || constants.MAX_CONCURENT_REQUEST;
            recordTableUtils.FlowControlObject.call(this, maxRequests);
        }

        return {
            updateRecordPage: updateRecordPage,
            genericErrorCatch: genericErrorCatch,
            readMainEntity: readMainEntity,
            getTableModel: getTableModel,
            FlowControlObject: FlowControlObject,
            pauseUpdateRecordPage: pauseUpdateRecordPage,
            resumeUpdateRecordPage: resumeUpdateRecordPage
        };
    }]);

})();
<|endoftext|>


<commit_before>;(function () {
    'use strict';

    var server = require('../../app'),
        testServer = require('supertest-as-promised')(server);

    describe('Request /book/save', function () {
        var _bookTest = {
                isbn: '123456789',
                title: 'Title book',
                description: 'Description book'
            },
            _assertBookProperties = function (book) {
                expect(book).to.have.property('isbn', '123456789');
                expect(book).to.have.property('title', 'Title book');
                expect(book).to.have.property('description', 'Description book');
                expect(book).to.have.property('id');
            };

        describe('[POST]', function () {
            it('Should create new book or update.', function (done) {
                testServer
                    .post('/api/book/save/')
                    .type('form')
                    .send({book: _bookTest})
                    .set('Accept', 'application/json')
                    .expect(201)
                    .then(function (res) {
                        var headerParams = res.body,
                            bookParams = headerParams.book;

                        expect(headerParams).to.have.property('book');
                        _assertBookProperties(bookParams);
                    })
                    .then(done)
                    .catch(console.log.bind(console));
            });
        });

        describe('[GET]', function () {
            it('Should retrieve an existance book.', function (done) {
                var idBook = null;

                // Create or update Book
                testServer
                    .post('/api/book/save/')
                    .type('form')
                    .send({book: _bookTest})
                    .set('Accept', /application\/json/)
                    .expect(201)
                    .then(function (res) {
                        idBook = res.body.book.id;

                        // get The Book cerated before
                        return testServer
                            .get('/api/book/id/' + idBook)
                            .expect(200)
                            .expect('Content-Type', /application\/json/);
                    })
                    .then(function (res) {
                        var headerParams = res.body,
                            bookParams = headerParams.book;

                        // Assertions ti Testing Book
                        expect(headerParams).to.have.property('book');
                        _assertBookProperties(bookParams);
                        expect(bookParams).to.have.property('id').to.equal(idBook);
                    })
                    .then(done)
                    .catch(console.log.bind(console));
            });
        });

        describe('[PUT]', function () {
            it('It shold update stored Book.', function (done) {
                var idBook = null;

                // Create or update Book [POST]
                testServer
                    .post('/api/book/save/')
                    .type('form')
                    .send({book: _bookTest})
                    .set('Accept', /application\/json/)
                    .expect(201)
                    .then(function (res) {
                        idBook = res.body.book.id;

                        // get The Book cerated before
                        return testServer
                            .get('/api/book/id/' + idBook)
                            .expect(200)
                            .expect('Content-Type', /application\/json/);
                    })
                    .then(function (res) {
                        var headerParams = res.body,
                            bookParams = headerParams.book;

                        bookParams.title = 'Title book for Nerds';
                        testServer
                            .put('/api/book/id/' + idBook)
                            .type('form')
                            .send({book: bookParams})
                            .expect(200)
                            .expect('Content-Type', /application\/json/);

                    })
                    .then(function (res) {
                        var headerParams = res.body,
                            bookParams = headerParams.book;

                        // Assertions ti Testing Book
                        expect(headerParams).to.have.property('book');
                        expect(bookParams).to.have.property('title').to.equals('Title book for Nerds');
                        expect(bookParams).to.have.property('id').to.equal(idBook);
                    })
                    .then(done)
                    .catch(console.log.bind(console));
            });
        });
    });
}());<commit_msg>testing delete restfull api books<commit_after>;(function () {
    'use strict';

    var server = require('../../app'),
        testServer = require('supertest-as-promised')(server);

    describe('Request /book/save', function () {
        var _bookTest = {
                isbn: '123456789',
                title: 'Title book',
                description: 'Description book'
            },
            _assertBookProperties = function (book) {
                expect(book).to.have.property('isbn', '123456789');
                expect(book).to.have.property('title', 'Title book');
                expect(book).to.have.property('description', 'Description book');
                expect(book).to.have.property('id');
            };

        describe('[POST]', function () {
            it('Should create new book or update.', function (done) {
                testServer
                    .post('/api/book/save/')
                    .type('form')
                    .send({book: _bookTest})
                    .set('Accept', 'application/json')
                    .expect(201)
                    .then(function (res) {
                        var headerParams = res.body,
                            bookParams = headerParams.book;

                        expect(headerParams).to.have.property('book');
                        _assertBookProperties(bookParams);
                    })
                    .then(done)
                    .catch(console.log.bind(console));
            });
        });

        describe('[GET]', function () {
            it('Should retrieve an existance book.', function (done) {
                var idBook = null;

                // Create or update Book
                testServer
                    .post('/api/book/save/')
                    .type('form')
                    .send({book: _bookTest})
                    .set('Accept', /application\/json/)
                    .expect(201)
                    .then(function (res) {
                        idBook = res.body.book.id;

                        // get The Book cerated before
                        return testServer
                            .get('/api/book/id/' + idBook)
                            .expect(200)
                            .expect('Content-Type', /application\/json/);
                    })
                    .then(function (res) {
                        var headerParams = res.body,
                            bookParams = headerParams.book;

                        // Assertions ti Testing Book
                        expect(headerParams).to.have.property('book');
                        _assertBookProperties(bookParams);
                        expect(bookParams).to.have.property('id').to.equal(idBook);
                    })
                    .then(done)
                    .catch(console.log.bind(console));
            });
        });

        describe('[PUT]', function () {
            it('It shold update stored Book.', function (done) {
                var idBook = null;

                // Create or update Book [POST]
                testServer
                    .post('/api/book/save/')
                    .type('form')
                    .send({book: _bookTest})
                    .set('Accept', /application\/json/)
                    .expect(201)
                    .then(function (res) {
                        idBook = res.body.book.id;

                        // get The Book cerated before
                        return testServer
                            .get('/api/book/id/' + idBook)
                            .expect(200)
                            .expect('Content-Type', /application\/json/);
                    })
                    .then(function (res) {
                        var headerParams = res.body,
                            bookParams = headerParams.book;

                        bookParams.title = 'Title book for Nerds';
                        testServer
                            .put('/api/book/id/' + idBook)
                            .type('form')
                            .send({book: bookParams})
                            .expect(200)
                            .expect('Content-Type', /application\/json/);

                    })
                    .then(function (res) {
                        var headerParams = res.body,
                            bookParams = headerParams.book;

                        // Assertions Testing Book
                        expect(headerParams).to.have.property('book');
                        expect(bookParams).to.have.property('title').to.equals('Title book for Nerds');
                        expect(bookParams).to.have.property('id').to.equal(idBook);
                    })
                    .then(done)
                    .catch(console.log.bind(console));
            });
        });

        describe('[DELETE]', function () {
            it('It should delete a book', function (done) {
                 var idBook = null;

                // Create or update Book [POST]
                testServer
                    .post('/api/book/save/')
                    .type('form')
                    .send({book: _bookTest})
                    .set('Accept', /application\/json/)
                    .expect(201)
                    .then(function (res) {
                        idBook = res.body.book.id;

                        // get The Book cerated before
                        return testServer
                            .get('/api/book/id/' + idBook)
                            .expect(200)
                            .expect('Content-Type', /application\/json/);
                    })
                    .then(function (res) {
                        var headerParams = res.body,
                            bookParams = headerParams.book;

                        bookParams.title = 'Title book for Nerds';
                        // remove a book [DELETE]
                        testServer
                            .delete('/api/book/id/' + idBook)
                            .expect(200);
                    })
                    .then(function () {
                        testServer
                            .get('/api/book/id/' + idBook)
                            .expect(404);
                    })
                    .then(done)
                    .catch(console.log.bind(console));
            });
        });
    });
}());<|endoftext|>


<commit_before>var renderer, scene, camera, gui, container, stats,
    begin = new THREE.Vector3( 0, 0, 0 ),
    dir = new THREE.Vector3( 0, 1, 0 ),
    angle = 13,
    length = 2,
    depth = 2;

function drawLine( start, end, width ) {
    var material = new THREE.LineBasicMaterial( {
        color: randColour(),
        linewidth: width
    } );

    var geometry = new THREE.Geometry();
    geometry.vertices.push( end );
    geometry.vertices.push( start );

    var line = new THREE.Line( geometry, material );
    scene.add( line );
}

function randColour() {
    var colour = ('0x' + parseInt(randInt(75, 255)).toString(16) +
        parseInt(randInt(75, 255)).toString(16) +
        parseInt(randInt(75, 255)).toString(16));
    return parseInt(colour);
}

function randInt( min, max ) {
    return Math.floor(Math.random() * ((max-min)+1.0) + min);
}

function randFloat( min, max ) {
    return Math.random() * ((max-min)+1.0) + min;
}

function vectorOnCircle( rad ) {
    var x = randFloat(-rad, rad),
        z = randFloat(-rad, rad),
        vec = new THREE.Vector3(x, 0, z);
    vec.normalize();
    vec * rad;
    return vec;
}

function deg2rad( deg ) {
    return deg * Math.PI/180;
}

function rad2deg( rad ) {
    return rad * 180/Math.PI;
}

function aimY( growthDir ) {
    var out = new Array(),
        xAngle,
        zAngle;

    var xyLength = Math.sqrt(growthDir.x * growthDir.x + growthDir.y * growthDir.y),
        vecLength = Math.sqrt(growthDir.x * growthDir.x + growthDir.y * growthDir.y + growthDir.z * growthDir.z);

    if ( xyLength == 0 ) {
        zAngle = growthDir.x > 0 ? deg2rad(90) : deg2rad(-90);
    } else {
        zAngle = Math.acos((growthDir.y)/xyLength);
    }

    xAngle = Math.acos(xyLength/vecLength);

    xAngle = growthDir.z > 0 ? xAngle : -xAngle;
    out.push(rad2deg(xAngle));

    zAngle = growthDir.x > 0 ? -zAngle : zAngle;
    out.push(rad2deg(zAngle));
    return out;
}

function tree( depth, base, growthDir, branchAngle, branchLen ) {
    if (depth <= 0 ){return;}

    var rad = branchLen * Math.sin( deg2rad(branchAngle) );
    var vec = vectorOnCircle( rad );
    vec.y = branchLen;

    var xaxis = new THREE.Vector3( 1, 0, 0 ),
        zaxis = new THREE.Vector3( 0, 0, 1 );

    var angle = aimY(growthDir);
    vec.applyAxisAngle( xaxis, deg2rad(angle[0]) );
    vec.applyAxisAngle( zaxis, deg2rad(angle[1]) );

    var end = new THREE.Vector3( vec.x+base.x, Math.abs(vec.y)+base.y, vec.z+base.z );
    var reflectedVec = new THREE.Vector3( vec.x, vec.y, vec.z );
    reflectedVec.multiplyScalar( -1 );
    reflectedVec.reflect( growthDir );
    reflectedVec.y = Math.abs( reflectedVec.y );
    reflectedVec.normalize();
    var end2 = new THREE.Vector3( (base.x+reflectedVec.x), (base.y+reflectedVec.y), (base.z+reflectedVec.z) );
    drawLine( base, end, depth );
    drawLine( base, end2, depth );
    tree( depth-1, end, vec, branchAngle, branchLen*0.6 );
    tree( depth-1, end2, reflectedVec, branchAngle, branchLen*0.6 );
}

function setupRenderer() {
    renderer = new THREE.WebGLRenderer( { antialias: false } );
    renderer.setSize( window.innerWidth, window.innerHeight );
    container = document.getElementById( 'container' );
    container.appendChild( renderer.domElement );
    stats = new Stats();
    stats.domElement.style.position = 'absolute';
    stats.domElement.style.top = '0px';
    stats.domElement.style.right = '35px';
    stats.domElement.style.zIndex = 100;
    container.appendChild( stats.domElement );
}

function clearTree() {
    count = scene.children.length;
    for (var i=count;i>=0;i--) {
        child = scene.children[i];
        if ( child instanceof THREE.GridHelper ) {continue;}
        if ( child instanceof THREE.Line ) {
            scene.remove(child);
        }
    }
}

function updateTree( controls ) {
    clearTree();
    tree( controls.count, begin, dir, controls.angle, controls.length );
}

function render() {
    renderer.render( scene, camera );
}

function init() {
    scene = new THREE.Scene();
    var grid = new THREE.GridHelper( 10, 2.5 )
    scene.add( grid );
    setupRenderer();

    var aspect = window.innerWidth / window.innerHeight;
    camera = new THREE.PerspectiveCamera( 60, aspect, 1, 50 );
    orbit = new THREE.OrbitControls( camera, container );
    orbit.addEventListener( 'change', render );
    camera.position.z = 10;
    camera.position.y = 5;
    camera.lookAt( new THREE.Vector3( 0, 0, 0 ) );
    camera.updateProjectionMatrix();

    var Controls = function() {
        this.angle = angle;
        this.length = length;
        this.count = depth;
        this.update = function () {updateTree(this)};
    };

    var controls = new Controls();
    gui = new dat.GUI();
    gui.add( controls, 'angle' );
    gui.add( controls, 'length', 1 );
    gui.add( controls, 'count', 1 ).step( 1 );
    gui.add( controls, 'update');
    gui.domElement.style.paddingTop = '55px';
    window.addEventListener( 'resize', onWindowResize, false );
}

function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize( window.innerWidth, window.innerHeight );
}

function animate() {
    requestAnimationFrame( animate );
    render();
    stats.update();
}

init();
tree( depth, begin, dir, angle, length );
animate();
<commit_msg>making the count control "texan proof"<commit_after>var renderer, scene, camera, gui, container, stats,
    begin = new THREE.Vector3( 0, 0, 0 ),
    dir = new THREE.Vector3( 0, 1, 0 ),
    angle = 13,
    length = 2,
    depth = 2;

function drawLine( start, end, width ) {
    var material = new THREE.LineBasicMaterial( {
        color: randColour(),
        linewidth: width
    } );

    var geometry = new THREE.Geometry();
    geometry.vertices.push( end );
    geometry.vertices.push( start );

    var line = new THREE.Line( geometry, material );
    scene.add( line );
}

function randColour() {
    var colour = ('0x' + parseInt(randInt(75, 255)).toString(16) +
        parseInt(randInt(75, 255)).toString(16) +
        parseInt(randInt(75, 255)).toString(16));
    return parseInt(colour);
}

function randInt( min, max ) {
    return Math.floor(Math.random() * ((max-min)+1.0) + min);
}

function randFloat( min, max ) {
    return Math.random() * ((max-min)+1.0) + min;
}

function vectorOnCircle( rad ) {
    var x = randFloat(-rad, rad),
        z = randFloat(-rad, rad),
        vec = new THREE.Vector3(x, 0, z);
    vec.normalize();
    vec * rad;
    return vec;
}

function deg2rad( deg ) {
    return deg * Math.PI/180;
}

function rad2deg( rad ) {
    return rad * 180/Math.PI;
}

function aimY( growthDir ) {
    var out = new Array(),
        xAngle,
        zAngle;

    var xyLength = Math.sqrt(growthDir.x * growthDir.x + growthDir.y * growthDir.y),
        vecLength = Math.sqrt(growthDir.x * growthDir.x + growthDir.y * growthDir.y + growthDir.z * growthDir.z);

    if ( xyLength == 0 ) {
        zAngle = growthDir.x > 0 ? deg2rad(90) : deg2rad(-90);
    } else {
        zAngle = Math.acos((growthDir.y)/xyLength);
    }

    xAngle = Math.acos(xyLength/vecLength);

    xAngle = growthDir.z > 0 ? xAngle : -xAngle;
    out.push(rad2deg(xAngle));

    zAngle = growthDir.x > 0 ? -zAngle : zAngle;
    out.push(rad2deg(zAngle));
    return out;
}

function tree( depth, base, growthDir, branchAngle, branchLen ) {
    if (depth <= 0 ){return;}

    var rad = branchLen * Math.sin( deg2rad(branchAngle) );
    var vec = vectorOnCircle( rad );
    vec.y = branchLen;

    var xaxis = new THREE.Vector3( 1, 0, 0 ),
        zaxis = new THREE.Vector3( 0, 0, 1 );

    var angle = aimY(growthDir);
    vec.applyAxisAngle( xaxis, deg2rad(angle[0]) );
    vec.applyAxisAngle( zaxis, deg2rad(angle[1]) );

    var end = new THREE.Vector3( vec.x+base.x, Math.abs(vec.y)+base.y, vec.z+base.z );
    var reflectedVec = new THREE.Vector3( vec.x, vec.y, vec.z );
    reflectedVec.multiplyScalar( -1 );
    reflectedVec.reflect( growthDir );
    reflectedVec.y = Math.abs( reflectedVec.y );
    reflectedVec.normalize();
    var end2 = new THREE.Vector3( (base.x+reflectedVec.x), (base.y+reflectedVec.y), (base.z+reflectedVec.z) );
    drawLine( base, end, depth );
    drawLine( base, end2, depth );
    tree( depth-1, end, vec, branchAngle, branchLen*0.6 );
    tree( depth-1, end2, reflectedVec, branchAngle, branchLen*0.6 );
}

function setupRenderer() {
    renderer = new THREE.WebGLRenderer( { antialias: false } );
    renderer.setSize( window.innerWidth, window.innerHeight );
    container = document.getElementById( 'container' );
    container.appendChild( renderer.domElement );
    stats = new Stats();
    stats.domElement.style.position = 'absolute';
    stats.domElement.style.top = '0px';
    stats.domElement.style.right = '35px';
    stats.domElement.style.zIndex = 100;
    container.appendChild( stats.domElement );
}

function clearTree() {
    count = scene.children.length;
    for (var i=count;i>=0;i--) {
        child = scene.children[i];
        if ( child instanceof THREE.GridHelper ) {continue;}
        if ( child instanceof THREE.Line ) {
            scene.remove(child);
        }
    }
}

function updateTree( controls ) {
    clearTree();
    tree( controls.count, begin, dir, controls.angle, controls.length );
}

function render() {
    renderer.render( scene, camera );
}

function init() {
    scene = new THREE.Scene();
    var grid = new THREE.GridHelper( 10, 2.5 )
    scene.add( grid );
    setupRenderer();

    var aspect = window.innerWidth / window.innerHeight;
    camera = new THREE.PerspectiveCamera( 60, aspect, 1, 50 );
    orbit = new THREE.OrbitControls( camera, container );
    orbit.addEventListener( 'change', render );
    camera.position.z = 10;
    camera.position.y = 5;
    camera.lookAt( new THREE.Vector3( 0, 0, 0 ) );
    camera.updateProjectionMatrix();

    var Controls = function() {
        this.angle = angle;
        this.length = length;
        this.count = depth;
        this.update = function () {updateTree(this)};
    };

    var controls = new Controls();
    gui = new dat.GUI();
    gui.add( controls, 'angle' );
    gui.add( controls, 'length', 1 );
    gui.add( controls, 'count', 1, 15 ).step( 1 );
    gui.add( controls, 'update');
    gui.domElement.style.paddingTop = '55px';
    window.addEventListener( 'resize', onWindowResize, false );
}

function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize( window.innerWidth, window.innerHeight );
}

function animate() {
    requestAnimationFrame( animate );
    render();
    stats.update();
}

init();
tree( depth, begin, dir, angle, length );
animate();
<|endoftext|>


<commit_before>var pkg = require('./package.json')
var fs  = require('fs')

function minor(a) {
  return [a[0], a[1], Number(a[2]) + 1] }

function feature(a) {
  return [a[0], Number(a[1]) + 1, 0] }

function major(a) {
  return [Number(a[0]) + 1, 0, 0] }

function bump(what, version) {
  return what === 'MAJOR'?    major(version)
  :      what === 'FEATURE'?  feature(version)
  :      /* otherwise */      minor(version) }

pkg.version = bump(process.argv[2], pkg.version.split('.')).join('.')
// fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2))
console.log(pkg.version)
<commit_msg>Points to the correct package.json<commit_after>var pkg = require('../package.json')
var fs  = require('fs')

function minor(a) {
  return [a[0], a[1], Number(a[2]) + 1] }

function feature(a) {
  return [a[0], Number(a[1]) + 1, 0] }

function major(a) {
  return [Number(a[0]) + 1, 0, 0] }

function bump(what, version) {
  return what === 'MAJOR'?    major(version)
  :      what === 'FEATURE'?  feature(version)
  :      /* otherwise */      minor(version) }

pkg.version = bump(process.argv[2], pkg.version.split('.')).join('.')
// fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2))
console.log(pkg.version)
<|endoftext|>


<commit_before>(function( $ ) {

$.widget("ui.timepicker", {
    // default options
    options: {
        template: '

',
        timepicker_selector: "#ui-timepicker",
        offset: {
            top: 0
        },
        time_list: [
            'now',
            '18:00',
            '18:30',
            '19:00',
            '19:30',
            '20:00',
            '20:30',
            '21:00',
            '21:30',
            '22:00',
            '22:30',
            '23:00',
            '23:30'
        ]
    },

    _create: function() {
        //console.log("JO!", this.element)
        //this.element.after('<button type="button" class="ui-timepicker-trigger"></button>');
        //this.element.next().click(function() {
        //    alert("JOJO!!!")
        //});
        var self = this,
            options = self.options;

        this._init();

        this.element.addClass("hasTimepicker");
        this.button = $('<button type="button" class="ui-timepicker-trigger"></button>');
        this.element.after(this.button);

        // get/create timepicker
        this.timepicker = $(options.timepicker_selector);
        this.timepicker.hide();

        if (this.element.attr("disabled")) {
            this.button.attr("disabled", true);
        } else {
            // register events
            this.button.click(function() {
                self._toggleTimepicker();
            });
        }

        //this.element.focus(function() {
        //    self._toggleTimepicker();
        //})
    },

    _init: function() {
        if ($(this.options.timepicker_selector).size() > 0) return;

        var self = this,
            options = self.options,
            template = $(options.template),
            template_str = "
";

        for (var i = 0; i < options.time_list.length; i++) {
            if (options.time_list[i] == "now") {
                template_str += '
' + options.time_list[i] + '
';
            } else {
                template_str += '
' + options.time_list[i] + '
';
            }
        }
        template_str += "
";
        template.append(template_str);

        template.appendTo("body").find('li').click(function() {
            $(this).parent().children('li').removeClass("ui-state-active");
            $(this).addClass("ui-state-active");
            var new_val = $(this).html();
            if (new_val == "now") {
                var now = new Date(),
                    hours = now.getHours(),
                    minutes = now.getMinutes();

                hours = ((hours < 10) ? "0" + hours : hours);
                minutes = ((minutes < 10) ? "0" + minutes : minutes);

                new_val = hours + ":" + minutes;
            }
            $(self.timepicker.data("current_input")).val(new_val);
            self.timepicker.hide();
        });

        $(document).mousedown(function(evt) {
            if (self.timepicker.is(":visible")) {
                var $target = $(evt.target);
                if ($target[0].id != self.timepicker[0].id && $target.parents(options.timepicker_selector).length == 0 && !$target.hasClass('hasTimepicker') && !$target.hasClass('ui-timepicker-trigger')) {
                    self.timepicker.hide();
                }
            }
        });
    },

    _toggleTimepicker: function() {
        if (this.timepicker.is(":visible")) {
            this.timepicker.data("current_input", null);
            this.timepicker.hide();
        } else {
            this.timepicker_offset = this.element.offset();
            //this.timepicker_offset.left += this.element.outerWidth()
            this.timepicker_offset.top += this.element.outerHeight() + this.options.offset.top;
            this.timepicker.css(this.timepicker_offset);
            this.timepicker.data("current_input", this.element);
            this.element.focus();
            this.timepicker.show();
        }
        this.timepicker_open = !this.timepicker_open;
    },

    value: function() {
        // calculate some value and return it
        return 25;
    },

    length: function() {
        return 22;
    },

    destroy: function() {
        $.Widget.prototype.destroy.apply(this, arguments); // default destroy
        // now do other stuff particular to this widget
    }
});

})(jQuery.noConflict());<commit_msg>fixes issue 213<commit_after>(function( $ ) {

$.widget("ui.timepicker", {
    // default options
    options: {
        template: '

',
        timepicker_selector: "#ui-timepicker",
        offset: {
            top: 0
        },
        time_list: [
            'now',
            '00:00',
            '01:00',
            '02:00',
            '03:00',
            '04:00',
            '05:00',
            '06:00',
            '07:00',
            '08:00',
            '09:00',
            '10:00',
            '11:00',
            '12:00',
            '13:00',
            '14:00',
            '15:00',
            '16:00',
            '17:00',
            '18:00',
            '19:00',
            '20:00',
            '21:00',
            '22:00',
            '23:00'
        ]
    },

    _create: function() {
        //console.log("JO!", this.element)
        //this.element.after('<button type="button" class="ui-timepicker-trigger"></button>');
        //this.element.next().click(function() {
        //    alert("JOJO!!!")
        //});
        var self = this,
            options = self.options;

        this._init();

        this.element.addClass("hasTimepicker");
        this.button = $('<button type="button" class="ui-timepicker-trigger"></button>');
        this.element.after(this.button);

        // get/create timepicker
        this.timepicker = $(options.timepicker_selector);
        this.timepicker.hide();

        if (this.element.attr("disabled")) {
            this.button.attr("disabled", true);
        } else {
            // register events
            this.button.click(function() {
                self._toggleTimepicker();
            });
        }

        //this.element.focus(function() {
        //    self._toggleTimepicker();
        //})
    },

    _init: function() {
        if ($(this.options.timepicker_selector).size() > 0) return;

        var self = this,
            options = self.options,
            template = $(options.template),
            template_str = "
";

        for (var i = 0; i < options.time_list.length; i++) {
            if (options.time_list[i] == "now") {
                template_str += '
' + options.time_list[i] + '
';
            } else {
                template_str += '
' + options.time_list[i] + '
';
            }
        }
        template_str += "
";
        template.append(template_str);

        template.appendTo("body").find('li').click(function() {
            $(this).parent().children('li').removeClass("ui-state-active");
            $(this).addClass("ui-state-active");
            var new_val = $(this).html();
            if (new_val == "now") {
                var now = new Date(),
                    hours = now.getHours(),
                    minutes = now.getMinutes();

                hours = ((hours < 10) ? "0" + hours : hours);
                minutes = ((minutes < 10) ? "0" + minutes : minutes);

                new_val = hours + ":" + minutes;
            }
            $(self.timepicker.data("current_input")).val(new_val);
            self.timepicker.hide();
        });

        $(document).mousedown(function(evt) {
            if (self.timepicker.is(":visible")) {
                var $target = $(evt.target);
                if ($target[0].id != self.timepicker[0].id && $target.parents(options.timepicker_selector).length == 0 && !$target.hasClass('hasTimepicker') && !$target.hasClass('ui-timepicker-trigger')) {
                    self.timepicker.hide();
                }
            }
        });
    },

    _toggleTimepicker: function() {
        if (this.timepicker.is(":visible")) {
            this.timepicker.data("current_input", null);
            this.timepicker.hide();
        } else {
            this.timepicker_offset = this.element.offset();
            //this.timepicker_offset.left += this.element.outerWidth()
            this.timepicker_offset.top += this.element.outerHeight() + this.options.offset.top;
            this.timepicker.css(this.timepicker_offset);
            this.timepicker.data("current_input", this.element);
            this.element.focus();
            this.timepicker.show();
        }
        this.timepicker_open = !this.timepicker_open;
    },

    value: function() {
        // calculate some value and return it
        return 25;
    },

    length: function() {
        return 22;
    },

    destroy: function() {
        $.Widget.prototype.destroy.apply(this, arguments); // default destroy
        // now do other stuff particular to this widget
    }
});

})(jQuery.noConflict());<|endoftext|>


<commit_before>import React, { Component } from 'react';
import Syncano from 'syncano';
import Hello from './Hello';

export default (ComposedComponent) => (
  class AuthHOC extends Component {
    constructor(props) {
      super(props);

      this.state = {
        status: null,
        message: null,
        displayValidationErrors: false
      };
    };

    static childContextTypes = {
      auth: React.PropTypes.object
    };

    getChildContext = () => {
      const auth = {
        status: this.state.status,
        message: this.state.message,
        resetStatus: this.resetStatus,
        displayValidationErrors: this.state.displayValidationErrors,
        showValidationErrors: this.showValidationErrors,
        hideValidationErrors: this.hideValidationErrors,
        handlePasswordAuth: this.handlePasswordAuth,
        handlePasswordReset: this.handlePasswordReset,
        handleSocialAuth: this.handleSocialAuth
      };

      return { auth };
    };

    setStatus = (status, message) => {
      this.setState({ status, message });
    };

    resetStatus = () => {
      this.setState({
        status: null,
        message: null
      });
    };

    handlePasswordAuth = (type, { email, password }) => {
      const { Account } = Syncano({ baseUrl: APP_CONFIG.syncanoAPIBaseUrl });

      this.resetStatus();
      this.setStatus('processing');

      Account[type]({ email, password })
        .then((data) => {
          this.trackSignUp(
            { ...data, type },
            () => this.redirectToDashboard(data.account_key, type === 'register' && true)
          );
        })
        .catch((error) => this.setStatus(error.status, error.message));
    };

    handlePasswordReset = ({ email }) => {
      const { Account } = Syncano({ baseUrl: APP_CONFIG.syncanoAPIBaseUrl });

      this.resetStatus();
      this.setStatus('processing');

      Account.resetPassword(email)
        .then((data) => this.setState({ status: 'success' }))
        .catch((error) => this.setStatus(error.status, error.message));
    };

    handleSocialAuth = (network) => {
      const { Account } = Syncano({ baseUrl: APP_CONFIG.syncanoAPIBaseUrl });

      Hello(network).login().then((data) => {
        const { access_token } = data.authResponse;

        if (data.network === 'google') {
          data.network = 'google-oauth2';
        }

        Account.socialLogin(data.network, access_token)
          .then((response) => {
            this.trackSignUp(
              { ...response, network: data.network },
              () => this.redirectToDashboard(response.account_key, response.created)
            );
          })
          .catch((error) => this.setStatus(error.status, error.message));
      }, (error) => this.setState({
        message: error.error.message
      }));
    };

    trackSignUp(data, callback) {
      const authBackend = data.network || 'password';
      const email = data.email;

      if (data.created || data.type === 'register') {
        window.analytics.alias(email);
        window.analytics.track('Sign Up Website', { authBackend, email }, {}, callback);
        return;
      }

      return callback();
    };

    redirectToDashboard = (token, signUpMode = false) => {
      let redirectUrl = `${APP_CONFIG.dashboardUrl}#/login?token=${token}`;

      if (signUpMode) {
        redirectUrl += `&signUpMode=${signUpMode}`;
      }

      window.location.href = redirectUrl;
    };

    showValidationErrors = () => {
      this.setState({ displayValidationErrors: true })
    };

    hideValidationErrors = () => {
      this.setState({ displayValidationErrors: false })
    };

    render() {
      return (
        <ComposedComponent {...this.props} />
      );
    };
  }
);
<commit_msg>[WEB-476] Add missing method<commit_after>import React, { Component } from 'react';
import Syncano from 'syncano';
import Hello from './Hello';

export default (ComposedComponent) => (
  class AuthHOC extends Component {
    constructor(props) {
      super(props);

      this.state = {
        status: null,
        message: null,
        displayValidationErrors: false
      };
    };

    static childContextTypes = {
      auth: React.PropTypes.object
    };

    getChildContext = () => {
      const auth = {
        status: this.state.status,
        message: this.state.message,
        resetStatus: this.resetStatus,
        displayValidationErrors: this.state.displayValidationErrors,
        showValidationErrors: this.showValidationErrors,
        hideValidationErrors: this.hideValidationErrors,
        handlePasswordAuth: this.handlePasswordAuth,
        handlePasswordReset: this.handlePasswordReset,
        handleSocialAuth: this.handleSocialAuth
      };

      return { auth };
    };

    setStatus = (status, message) => {
      this.setState({ status, message });
    };

    resetStatus = () => {
      this.setState({
        status: null,
        message: null
      });
    };

    handlePasswordAuth = (type, { email, password }) => {
      const { Account } = Syncano({ baseUrl: APP_CONFIG.syncanoAPIBaseUrl });

      this.resetStatus();
      this.setStatus('processing');

      Account[type]({ email, password })
        .then((data) => {
          this.trackSignUp(
            { ...data, type },
            () => this.redirectToDashboard(data.account_key, type === 'register' && true)
          );
        })
        .catch((error) => this.setStatus(error.status, error.message));
    };

    handlePasswordReset = ({ email }) => {
      const { Account } = Syncano({ baseUrl: APP_CONFIG.syncanoAPIBaseUrl });

      this.resetStatus();
      this.setStatus('processing');

      Account.resetPassword(email)
        .then((data) => this.setState({ status: 'success' }))
        .catch((error) => this.setStatus(error.status, error.message));
    };

    handleSocialAuth = (network) => {
      const { Account } = Syncano({ baseUrl: APP_CONFIG.syncanoAPIBaseUrl });

      Hello(network).login().then((data) => {
        const { access_token } = data.authResponse;

        if (data.network === 'google') {
          data.network = 'google-oauth2';
        }

        Account.socialLogin(data.network, access_token)
          .then((response) => {
            this.trackSignUp(
              { ...response, network: data.network },
              () => this.redirectToDashboard(response.account_key, response.created)
            );
          })
          .catch((error) => this.setStatus(error.status, error.message));
      }, (error) => this.setState({
        message: error.error.message
      }));
    };

    trackSignUp(data, callback) {
      const authBackend = data.network || 'password';
      const email = data.email;

      if (data.created || data.type === 'register') {
        window.analytics.alias(email);
        window.analytics.track('Sign Up Website', { authBackend, email }, {}, callback);
        return;
      }

      window.analytics.identify(email);
      return callback();
    };

    redirectToDashboard = (token, signUpMode = false) => {
      let redirectUrl = `${APP_CONFIG.dashboardUrl}#/login?token=${token}`;

      if (signUpMode) {
        redirectUrl += `&signUpMode=${signUpMode}`;
      }

      window.location.href = redirectUrl;
    };

    showValidationErrors = () => {
      this.setState({ displayValidationErrors: true })
    };

    hideValidationErrors = () => {
      this.setState({ displayValidationErrors: false })
    };

    render() {
      return (
        <ComposedComponent {...this.props} />
      );
    };
  }
);
<|endoftext|>


<commit_before>/*
 * basic-list-box
 */

import ElementBase from '../../../element-base/src/ElementBase';
import Super from './Super';
import ChildrenContent from '../../mixins/ChildrenContent';
import ClickSelection from '../../mixins/ClickSelection';
import ContentItems from '../../mixins/ContentItems';
import DirectionSelection from '../../mixins/DirectionSelection';
import Generic from '../../mixins/Generic';
import ItemSelection from '../../mixins/ItemSelection';
import ItemsAccessible from '../../mixins/ItemsAccessible';
import Keyboard from '../../mixins/Keyboard';
import KeyboardDirection from '../../mixins/KeyboardDirection';
import KeyboardPaging from '../../mixins/KeyboardPaging';
import KeyboardPrefixSelection from '../../mixins/KeyboardPrefixSelection';
import SelectionHighlight from '../../mixins/SelectionHighlight';
import SelectionScroll from '../../mixins/SelectionScroll';

// We'd like to just subclass ElementBase, but then we won't have access to
// this.ListBox.super. That only gets created by Extensible.extend(), so we have
// to manually invoke that. We could fix that if we could implement a component
// constructor, but currently document.registerElement() in Blink doesn't invoke
// a component constructor.
//class ListBox extends ElementBase {

class ListBox {

  // createdCallback() {
  //   let base = this.ListBox.super.createdCallback;
  //   if (base) {
  //     base.call(this);
  //   }
  //   this.log("createdCallback");
  // }

  // Stub for collectives for now
  get innermostAttached() {
    return this.$.itemsContainer;
  }
  get outermostAttached() {
    return this;
  }

  get template() {
    return `

        <content></content>

    `;
  }

}

// See notes above for class declaration.
ListBox = ElementBase.extend(
  Super,
  ChildrenContent,
  ClickSelection,
  ContentItems,
  DirectionSelection,
  Generic,
  ItemSelection,
  ItemsAccessible,
  Keyboard,
  KeyboardDirection,
  KeyboardPaging,
  KeyboardPrefixSelection,
  SelectionHighlight,
  SelectionScroll,
  ListBox
);

document.registerElement('basic-list-box', ListBox);

export default ListBox;
<commit_msg>Clean up ListBox code a bit.<commit_after>/*
 * basic-list-box
 */

import ElementBase from '../../../element-base/src/ElementBase';
import Super from './Super';
import ChildrenContent from '../../mixins/ChildrenContent';
import ClickSelection from '../../mixins/ClickSelection';
import ContentItems from '../../mixins/ContentItems';
import DirectionSelection from '../../mixins/DirectionSelection';
import Generic from '../../mixins/Generic';
import ItemSelection from '../../mixins/ItemSelection';
import ItemsAccessible from '../../mixins/ItemsAccessible';
import Keyboard from '../../mixins/Keyboard';
import KeyboardDirection from '../../mixins/KeyboardDirection';
import KeyboardPaging from '../../mixins/KeyboardPaging';
import KeyboardPrefixSelection from '../../mixins/KeyboardPrefixSelection';
import SelectionHighlight from '../../mixins/SelectionHighlight';
import SelectionScroll from '../../mixins/SelectionScroll';

// We'd like to just subclass ElementBase, but then we won't have access to
// this.ListBox.super. That only gets created by Extensible.extend(), so we have
// to manually invoke that. We could fix that if we could implement a component
// constructor, but currently document.registerElement() in Blink doesn't invoke
// a component constructor.
//class ListBox extends ElementBase {

export default class ListBox {

  // Stub for collectives for now
  get innermostAttached() {
    return this.$.itemsContainer;
  }
  get outermostAttached() {
    return this;
  }

  get template() {
    return `

        <content></content>

    `;
  }

}

// See notes above for class declaration.
ListBox = ElementBase.extend(
  Super,
  ChildrenContent,
  ClickSelection,
  ContentItems,
  DirectionSelection,
  Generic,
  ItemSelection,
  ItemsAccessible,
  Keyboard,
  KeyboardDirection,
  KeyboardPaging,
  KeyboardPrefixSelection,
  SelectionHighlight,
  SelectionScroll,
  ListBox
);

document.registerElement('basic-list-box', ListBox);
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import axios from 'axios';
import { Formik } from 'formik';
import { Loading, RaffleForm, Results } from '.';

export class RaffleContainer extends Component {
  // eslint-disable-next-line react/sort-comp
  initialState = {
    error: '',
    winners: [],
  };

  state = this.initialState;

  componentDidMount() {
    this.setState({
      meetup: this.restore('meetup') || '',
      meetupApiKey: this.restore('meetupApiKey') || '',
    });
  }

  resetState = () => {
    this.setState({
      // reset some
      ...this.initialState,
      // keep the rest
      meetup: this.state.meetup,
      meetupApiKey: this.state.meetupApiKey,
    });
  };

  restore = key => {
    if (global.window.localStorage) {
      return global.window.localStorage.getItem(key);
    }
    return undefined;
  };

  preserve = (key, value) => {
    if (global.window.localStorage) {
      global.window.localStorage.setItem(key, value);
    }
  };

  render() {
    return (

        <Formik
          enableReinitialize
          initialValues={{
            meetup: this.state.meetup,
            count: 2,
            specificEventId: '',
            meetupApiKey: this.state.meetupApiKey,
          }}
          onSubmit={async (
            { meetup, count, specificEventId, meetupApiKey },
            { setSubmitting },
          ) => {
            setSubmitting(true);
            this.preserve('meetup', meetup);
            this.preserve('meetupApiKey', meetupApiKey);
            try {
              const response = await axios.get(
                'https://wkovacs64.lib.id/meetup-raffle/',
                {
                  params: {
                    meetup,
                    count,
                    specificEventId,
                    meetupApiKey,
                  },
                },
              );
              if (response.data && response.data.winners) {
                this.setState({ winners: response.data.winners });
              } else {
                throw new Error('Malformed response received.');
              }
            } catch (err) {
              this.setState({ error: err.message });
            }
            setSubmitting(false);
          }}
          render={({ handleSubmit, isSubmitting }) => {
            if (isSubmitting) {
              return (

                  <Loading className="h5 w5 dark-blue" />

              );
            }
            if (this.state.error || this.state.winners.length) {
              return (
                <Results
                  onReset={this.resetState}
                  onSubmit={handleSubmit}
                  {...this.state}
                />
              );
            }
            return <RaffleForm />;
          }}
        />

    );
  }
}

export default RaffleContainer;
<commit_msg>Refactor resetState and explicitly set default form field values<commit_after>import React, { Component } from 'react';
import axios from 'axios';
import { Formik } from 'formik';
import { Loading, RaffleForm, Results } from '.';

export class RaffleContainer extends Component {
  // eslint-disable-next-line react/sort-comp
  initialState = {
    error: '',
    winners: [],
  };

  state = this.initialState;

  componentDidMount() {
    this.setState({
      meetup: this.restore('meetup') || '',
      meetupApiKey: this.restore('meetupApiKey') || '',
    });
  }

  resetState = () => {
    this.setState(this.initialState);
  };

  restore = key => {
    if (global.window.localStorage) {
      return global.window.localStorage.getItem(key);
    }
    return undefined;
  };

  preserve = (key, value) => {
    if (global.window.localStorage) {
      global.window.localStorage.setItem(key, value);
    }
  };

  render() {
    return (

        <Formik
          enableReinitialize
          initialValues={{
            meetup: this.state.meetup || '',
            count: 2,
            specificEventId: '',
            meetupApiKey: this.state.meetupApiKey || '',
          }}
          onSubmit={async (
            { meetup, count, specificEventId, meetupApiKey },
            { setSubmitting },
          ) => {
            setSubmitting(true);
            this.preserve('meetup', meetup);
            this.preserve('meetupApiKey', meetupApiKey);
            try {
              const response = await axios.get(
                'https://wkovacs64.lib.id/meetup-raffle/',
                {
                  params: {
                    meetup,
                    count,
                    specificEventId,
                    meetupApiKey,
                  },
                },
              );
              if (response.data && response.data.winners) {
                this.setState({ winners: response.data.winners });
              } else {
                throw new Error('Malformed response received.');
              }
            } catch (err) {
              this.setState({ error: err.message });
            }
            setSubmitting(false);
          }}
          render={({ handleSubmit, isSubmitting }) => {
            if (isSubmitting) {
              return (

                  <Loading className="h5 w5 dark-blue" />

              );
            }
            if (this.state.error || this.state.winners.length) {
              return (
                <Results
                  onReset={this.resetState}
                  onSubmit={handleSubmit}
                  {...this.state}
                />
              );
            }
            return <RaffleForm />;
          }}
        />

    );
  }
}

export default RaffleContainer;
<|endoftext|>


<commit_before>const ora = require('ora')
const mainSpinner = ora({
  text: 'Moro',
  spinner: 'hearts',
  color: 'red'
})

module.exports = mainSpinner
<commit_msg>Add emojie to moro text<commit_after>const ora = require('ora')
const mainSpinner = ora({
  text: 'Moro \\o/ ',
  spinner: 'hearts',
  color: 'red'
})

module.exports = mainSpinner
<|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.Controller.extend({
  form: function() {
    return this.store.createRecord('ssh-key');
  },

  actions: {
    deleteKey: function(key) {
      this.store.find('ssh-key', key.id).then(function(key) {
        key.destroyRecord();
      });
    },
    save: function(deferred) {
      var self = this;

      // Create a new record based on the values in the form
      var key = this.get('key');
      var name = this.get('name');
      var sshKey = this.store.createRecord('ssh-key', {
        name: name,
        key: key
      });

      // validate the record
      sshKey.validate();

      if (sshKey.get('isValid')) {
        sshKey.save().then(function() {
          // resolve the deferred promise
          deferred.resolve();
          // clear the form
          self.set('key', '');
          self.set('name', '');
        });
      } else {
        deferred.reject();
        sshKey.destroyRecord();
      }
    }
  }
});
<commit_msg>Resolve the promise once we know the model is valid<commit_after>import Ember from 'ember';

export default Ember.Controller.extend({
  form: function() {
    return this.store.createRecord('ssh-key');
  },

  actions: {
    deleteKey: function(key) {
      this.store.find('ssh-key', key.id).then(function(key) {
        key.destroyRecord();
      });
    },
    save: function(deferred) {
      var self = this;

      // Create a new record based on the values in the form
      var key = this.get('key');
      var name = this.get('name');
      var sshKey = this.store.createRecord('ssh-key', {
        name: name,
        key: key
      });

      // validate the record
      sshKey.validate();

      if (sshKey.get('isValid')) {
        // resolve the deferred promise
        deferred.resolve();

        sshKey.save().then(function() {
          // clear the form
          self.set('key', '');
          self.set('name', '');
        });
      } else {
        deferred.reject();
        sshKey.destroyRecord();
      }
    }
  }
});
<|endoftext|>


<commit_before>/**
 * mini-event
 * Copyright 2013 Baidu Inc. All rights reserved.
 *
 * @ignore
 * @file 事件对象类
 * @author otakustay
 */
define(
    function (require) {
        var lib = require('./lib');

        function returnTrue() { return true; }
        function returnFalse() { return false; }

        /**
         * 事件对象类
         *
         * @constructor
         *
         * 3个重载：
         *      - `new Event(type)`
         *      - `new Event(args)`
         *      - `new Event(type, args)`
         * 只提供一个对象作为参数，则是`new Event(args)`的形式，需要加上type
         *
         * @param {string | Object} [type] 事件类型
         * @param {Mixed} [args] 事件中的数据
         */
        function Event(type, args) {
            // 3个重载：
            //
            // - `new Event(type)`
            // - `new Event(args)`
            // - `new Event(type, args)`

            // 如果第1个参数是对象，则就当是`new Event(args)`形式
            if (typeof type === 'object') {
                args = type;
                type = args.type;
            }

            if (typeof args === 'object') {
                lib.extend(this, args);
            }
            else if (args) {
                this.data = args;
            }

            if (type) {
                this.type = type;
            }
        }

        /**
         * 判断默认行为是否已被阻止
         *
         * @return {boolean}
         */
        Event.prototype.isDefaultPrevented = returnFalse;

        /**
         * 阻止默认行为
         */
        Event.prototype.preventDefault = function () {
            this.isDefaultPrevented = returnTrue;
        };

        /**
         * 判断事件传播是否已被阻止
         *
         * @return {boolean}
         */
        Event.prototype.isPropagationStopped = returnFalse;

        /**
         * 阻止事件传播
         */
        Event.prototype.stopPropagation = function () {
            this.isPropagationStopped = returnTrue;
        };

        /**
         * 判断事件的立即传播是否已被阻止
         *
         * @return {boolean}
         */
        Event.prototype.isImmediatePropagationStopped = returnFalse;

        /**
         * 立即阻止事件传播
         */
        Event.prototype.stopImmediatePropagation = function () {
            this.isImmediatePropagationStopped = returnTrue;

            this.stopPropagation();
        };

        var globalWindow = (function () { return this; }());

        /**
         * 从DOM事件对象生成一个Event对象
         *
         * @param {Event} domEvent DOM事件对象
         * @param {string} [type] 事件类型
         * @param {Mixed} [args] 事件数据
         * @return {Event}
         */
        Event.fromDOMEvent = function (domEvent, type, args) {
            domEvent = domEvent || globalWindow.event;

            var event = new Event(type, args);

            event.preventDefault = function () {
                if (domEvent.preventDefault) {
                    domEvent.preventDefault();
                }
                else {
                    domEvent.returnValue = false;
                }

                Event.prototype.preventDefault.call(this);
            };

            event.stopPropagation = function () {
                if (domEvent.stopPropagation) {
                    domEvent.stopPropagation();
                }
                else {
                    domEvent.cancelBubble = true;
                }

                Event.prototype.stopPropagation.call(this);
            };

            event.stopImmediatePropagation = function () {
                if (domEvent.stopImmediatePropagation) {
                    domEvent.stopImmediatePropagation();
                }

                Event.prototype.stopImmediatePropagation.call(this);
            };

            return event;
        };

        // 复制事件属性的时候不复制这几个
        var EVENT_PROPERTY_BLACK_LIST = {
            type: true,
            preventDefault: true, isDefaultPrevented: true,
            stopPropagation: true, isPropagationStopped: true,
            stopImmediatePropagation: true, isImmediatePropagationStopped: true
        };

        /**
         * 从一个已有事件对象生成一个新的事件对象
         *
         * @param {Event} originalEvent 作为源的已有事件对象
         * @param {Object} [options] 配置项
         * @param {string} [options.type] 新事件对象的类型，不提供则保留原类型
         * @param {boolean} [options.preserveData=false] 是否保留事件的信息
         * @param {boolean} [options.syncState=false] 是否让2个事件状态同步，
         * 状态包括**阻止传播**、**立即阻止传播**和**阻止默认行为**
         * @param {Object} [options.extend] 提供事件对象的更多属性
         */
        Event.fromEvent = function (originalEvent, options) {
            var defaults = {
                type: originalEvent.type,
                preserveData: false,
                syncState: false
            };
            options = lib.extend(defaults, options);

            var newEvent = new Event(options.type);
            // 如果保留数据，则把数据复制过去
            if (options.preserveData) {
                // 要去掉一些可能出现的杂质，因此不用`lib.extend`
                for (var key in originalEvent) {
                    if (originalEvent.hasOwnProperty(key)
                        && !EVENT_PROPERTY_BLACK_LIST.hasOwnProperty(key)
                    ) {
                        newEvent[key] = originalEvent[key];
                    }
                }
            }

            // 如果有扩展属性，加上去
            if (options.extend) {
                lib.extend(newEvent, options.extend);
            }

            // 如果要同步状态，把和状态相关的方法挂接上
            if (options.syncState) {
                newEvent.preventDefault = function () {
                    originalEvent.preventDefault();

                    Event.prototype.preventDefault.call(this);
                };

                newEvent.stopPropagation = function () {
                    originalEvent.stopPropagation();

                    Event.prototype.stopPropagation.call(this);
                };

                newEvent.stopImmediatePropagation = function () {
                    originalEvent.stopImmediatePropagation();

                    Event.prototype.stopImmediatePropagation.call(this);
                };
            }

            return newEvent;
        };

        /**
         * 将一个对象的事件代理到另一个对象
         *
         * @param {EventTarget} from 事件提供方
         * @param {EventTarget | string} fromType 为字符串表示提供方事件类型；
         * 为可监听对象则表示接收方，此时事件类型由第3个参数提供
         * @param {EventTarget | string} to 为字符串则表示提供方和接收方事件类型一致，
         * 由此参数作为事件类型；为可监听对象则表示接收方，此时第2个参数必须为字符串
         * @param {string} [toType] 接收方的事件类型
         * @param {Object} [options] 配置项
         * @param {boolean} [options.preserveData=false] 是否保留事件的信息
         * @param {boolean} [options.syncState=false] 是否让2个事件状态同步，
         * 状态包括**阻止传播**、**立即阻止传播**和**阻止默认行为**
         * @param {Object} [options.extend] 提供事件对象的更多属性
         *
         *     // 当`label`触发`click`事件时，自身也触发`click`事件
         *     Event.delegate(label, this, 'click');
         *
         *     // 当`label`触发`click`事件时，自身触发`labelclick`事件
         *     Event.delegate(label, 'click', this, 'labelclick');
         */
        Event.delegate = function (from, fromType, to, toType, options) {
            // 重载：
            //
            // 1. `.delegate(from, fromType, to, toType)`
            // 2. `.delegate(from, fromType, to, toType, options)`
            // 3. `.delegate(from, to, type)`
            // 4. `.delegate(from, to, type, options)

            // 重点在于第2个参数的类型，如果为字符串则肯定是1或2，否则为3或4
            var useDifferentType = typeof fromType === 'string';
            var source = {
                object: from,
                type: useDifferentType ? fromType : to
            };
            var target = {
                object: useDifferentType ? to : fromType,
                type: useDifferentType ? toType : to
            };
            var config = useDifferentType ? options : toType;
            config = lib.extend({ preserveData: false }, config);

            // 如果提供方不能注册事件，或接收方不能触发事件，那就不用玩了
            if (typeof source.object.on !== 'function'
                || typeof target.object.on !== 'function'
                || typeof target.object.fire !== 'function'
            ) {
                return;
            }

            var delegator = function (originalEvent) {
                var event = Event.fromEvent(originalEvent, config);

                target.object.fire(target.type, event);
            };

            source.object.on(source.type, delegator);
        };

        return Event;
    }
);
<commit_msg>代理事件时去除target属性<commit_after>/**
 * mini-event
 * Copyright 2013 Baidu Inc. All rights reserved.
 *
 * @ignore
 * @file 事件对象类
 * @author otakustay
 */
define(
    function (require) {
        var lib = require('./lib');

        function returnTrue() { return true; }
        function returnFalse() { return false; }

        /**
         * 事件对象类
         *
         * @constructor
         *
         * 3个重载：
         *      - `new Event(type)`
         *      - `new Event(args)`
         *      - `new Event(type, args)`
         * 只提供一个对象作为参数，则是`new Event(args)`的形式，需要加上type
         *
         * @param {string | Object} [type] 事件类型
         * @param {Mixed} [args] 事件中的数据
         */
        function Event(type, args) {
            // 3个重载：
            //
            // - `new Event(type)`
            // - `new Event(args)`
            // - `new Event(type, args)`

            // 如果第1个参数是对象，则就当是`new Event(args)`形式
            if (typeof type === 'object') {
                args = type;
                type = args.type;
            }

            if (typeof args === 'object') {
                lib.extend(this, args);
            }
            else if (args) {
                this.data = args;
            }

            if (type) {
                this.type = type;
            }
        }

        /**
         * 判断默认行为是否已被阻止
         *
         * @return {boolean}
         */
        Event.prototype.isDefaultPrevented = returnFalse;

        /**
         * 阻止默认行为
         */
        Event.prototype.preventDefault = function () {
            this.isDefaultPrevented = returnTrue;
        };

        /**
         * 判断事件传播是否已被阻止
         *
         * @return {boolean}
         */
        Event.prototype.isPropagationStopped = returnFalse;

        /**
         * 阻止事件传播
         */
        Event.prototype.stopPropagation = function () {
            this.isPropagationStopped = returnTrue;
        };

        /**
         * 判断事件的立即传播是否已被阻止
         *
         * @return {boolean}
         */
        Event.prototype.isImmediatePropagationStopped = returnFalse;

        /**
         * 立即阻止事件传播
         */
        Event.prototype.stopImmediatePropagation = function () {
            this.isImmediatePropagationStopped = returnTrue;

            this.stopPropagation();
        };

        var globalWindow = (function () { return this; }());

        /**
         * 从DOM事件对象生成一个Event对象
         *
         * @param {Event} domEvent DOM事件对象
         * @param {string} [type] 事件类型
         * @param {Mixed} [args] 事件数据
         * @return {Event}
         */
        Event.fromDOMEvent = function (domEvent, type, args) {
            domEvent = domEvent || globalWindow.event;

            var event = new Event(type, args);

            event.preventDefault = function () {
                if (domEvent.preventDefault) {
                    domEvent.preventDefault();
                }
                else {
                    domEvent.returnValue = false;
                }

                Event.prototype.preventDefault.call(this);
            };

            event.stopPropagation = function () {
                if (domEvent.stopPropagation) {
                    domEvent.stopPropagation();
                }
                else {
                    domEvent.cancelBubble = true;
                }

                Event.prototype.stopPropagation.call(this);
            };

            event.stopImmediatePropagation = function () {
                if (domEvent.stopImmediatePropagation) {
                    domEvent.stopImmediatePropagation();
                }

                Event.prototype.stopImmediatePropagation.call(this);
            };

            return event;
        };

        // 复制事件属性的时候不复制这几个
        var EVENT_PROPERTY_BLACK_LIST = {
            type: true, target: true,
            preventDefault: true, isDefaultPrevented: true,
            stopPropagation: true, isPropagationStopped: true,
            stopImmediatePropagation: true, isImmediatePropagationStopped: true
        };

        /**
         * 从一个已有事件对象生成一个新的事件对象
         *
         * @param {Event} originalEvent 作为源的已有事件对象
         * @param {Object} [options] 配置项
         * @param {string} [options.type] 新事件对象的类型，不提供则保留原类型
         * @param {boolean} [options.preserveData=false] 是否保留事件的信息
         * @param {boolean} [options.syncState=false] 是否让2个事件状态同步，
         * 状态包括**阻止传播**、**立即阻止传播**和**阻止默认行为**
         * @param {Object} [options.extend] 提供事件对象的更多属性
         */
        Event.fromEvent = function (originalEvent, options) {
            var defaults = {
                type: originalEvent.type,
                preserveData: false,
                syncState: false
            };
            options = lib.extend(defaults, options);

            var newEvent = new Event(options.type);
            // 如果保留数据，则把数据复制过去
            if (options.preserveData) {
                // 要去掉一些可能出现的杂质，因此不用`lib.extend`
                for (var key in originalEvent) {
                    if (originalEvent.hasOwnProperty(key)
                        && !EVENT_PROPERTY_BLACK_LIST.hasOwnProperty(key)
                    ) {
                        newEvent[key] = originalEvent[key];
                    }
                }
            }

            // 如果有扩展属性，加上去
            if (options.extend) {
                lib.extend(newEvent, options.extend);
            }

            // 如果要同步状态，把和状态相关的方法挂接上
            if (options.syncState) {
                newEvent.preventDefault = function () {
                    originalEvent.preventDefault();

                    Event.prototype.preventDefault.call(this);
                };

                newEvent.stopPropagation = function () {
                    originalEvent.stopPropagation();

                    Event.prototype.stopPropagation.call(this);
                };

                newEvent.stopImmediatePropagation = function () {
                    originalEvent.stopImmediatePropagation();

                    Event.prototype.stopImmediatePropagation.call(this);
                };
            }

            return newEvent;
        };

        /**
         * 将一个对象的事件代理到另一个对象
         *
         * @param {EventTarget} from 事件提供方
         * @param {EventTarget | string} fromType 为字符串表示提供方事件类型；
         * 为可监听对象则表示接收方，此时事件类型由第3个参数提供
         * @param {EventTarget | string} to 为字符串则表示提供方和接收方事件类型一致，
         * 由此参数作为事件类型；为可监听对象则表示接收方，此时第2个参数必须为字符串
         * @param {string} [toType] 接收方的事件类型
         * @param {Object} [options] 配置项
         * @param {boolean} [options.preserveData=false] 是否保留事件的信息
         * @param {boolean} [options.syncState=false] 是否让2个事件状态同步，
         * 状态包括**阻止传播**、**立即阻止传播**和**阻止默认行为**
         * @param {Object} [options.extend] 提供事件对象的更多属性
         *
         *     // 当`label`触发`click`事件时，自身也触发`click`事件
         *     Event.delegate(label, this, 'click');
         *
         *     // 当`label`触发`click`事件时，自身触发`labelclick`事件
         *     Event.delegate(label, 'click', this, 'labelclick');
         */
        Event.delegate = function (from, fromType, to, toType, options) {
            // 重载：
            //
            // 1. `.delegate(from, fromType, to, toType)`
            // 2. `.delegate(from, fromType, to, toType, options)`
            // 3. `.delegate(from, to, type)`
            // 4. `.delegate(from, to, type, options)

            // 重点在于第2个参数的类型，如果为字符串则肯定是1或2，否则为3或4
            var useDifferentType = typeof fromType === 'string';
            var source = {
                object: from,
                type: useDifferentType ? fromType : to
            };
            var target = {
                object: useDifferentType ? to : fromType,
                type: useDifferentType ? toType : to
            };
            var config = useDifferentType ? options : toType;
            config = lib.extend({ preserveData: false }, config);

            // 如果提供方不能注册事件，或接收方不能触发事件，那就不用玩了
            if (typeof source.object.on !== 'function'
                || typeof target.object.on !== 'function'
                || typeof target.object.fire !== 'function'
            ) {
                return;
            }

            var delegator = function (originalEvent) {
                var event = Event.fromEvent(originalEvent, config);
                // 修正`target`属性
                event.target = target.object;

                target.object.fire(target.type, event);
            };

            source.object.on(source.type, delegator);
        };

        return Event;
    }
);
<|endoftext|>